home *** CD-ROM | disk | FTP | other *** search
/ PC/CD Gamer UK 120 / CD Gamer Issue 120 (March 2003) (Disc 2).ISO / mods / Q2_Codered / codeRED1_0.exe / Data1.cab / menu.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-12-26  |  107.9 KB  |  4,010 lines

  1. /*
  2. Copyright (C) 1997-2001 Id Software, Inc.
  3.  
  4. This program is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU General Public License
  6. as published by the Free Software Foundation; either version 2
  7. of the License, or (at your option) any later version.
  8.  
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
  12.  
  13. See the GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with this program; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  18.  
  19. */
  20. #include <ctype.h>
  21. #ifdef _WIN32
  22. #include <io.h>
  23. #endif
  24. #include "client.h"
  25. #include "../client/qmenu.h"
  26.  
  27. static int    m_main_cursor;
  28.  
  29. #define NUM_CURSOR_FRAMES 15
  30.  
  31. static char *menu_in_sound        = "misc/menu1.wav";
  32. static char *menu_move_sound    = "misc/menu2.wav";
  33. static char *menu_out_sound        = "misc/menu3.wav";
  34.  
  35. void M_Menu_Main_f (void);
  36.     void M_Menu_Game_f (void);
  37.         void M_Menu_LoadGame_f (void);
  38.         void M_Menu_SaveGame_f (void);
  39.         void M_Menu_PlayerConfig_f (void);
  40.             void M_Menu_DownloadOptions_f (void);
  41.         void M_Menu_Credits_f( void );
  42.     void M_Menu_Multiplayer_f( void );
  43.         void M_Menu_JoinServer_f (void);
  44.             void M_Menu_AddressBook_f( void );
  45.         void M_Menu_StartServer_f (void);
  46.             void M_Menu_DMOptions_f (void);
  47.     void M_Menu_Video_f (void);
  48.     void M_Menu_Options_f (void);
  49.         void M_Menu_Keys_f (void);
  50.     void M_Menu_Quit_f (void);
  51.  
  52.     void M_Menu_Credits( void );
  53.  
  54. qboolean    m_entersound;        // play after drawing a frame, so caching
  55.                                 // won't disrupt the sound
  56.  
  57. void    (*m_drawfunc) (void);
  58. const char *(*m_keyfunc) (int key);
  59.  
  60. //=============================================================================
  61. /* Support Routines */
  62.  
  63. #define    MAX_MENU_DEPTH    8
  64.  
  65.  
  66. typedef struct
  67. {
  68.     void    (*draw) (void);
  69.     const char *(*key) (int k);
  70. } menulayer_t;
  71.  
  72. menulayer_t    m_layers[MAX_MENU_DEPTH];
  73. int        m_menudepth;
  74.  
  75. static void M_Banner( char *name )
  76. {
  77.     int w, h;
  78.  
  79.     Draw_GetPicSize (&w, &h, name );
  80.     Draw_Pic( viddef.width / 2 - w / 2, viddef.height / 2 - 110, name );
  81. }
  82.  
  83. void M_PushMenu ( void (*draw) (void), const char *(*key) (int k) )
  84. {
  85.     int        i;
  86.  
  87.     if (Cvar_VariableValue ("maxclients") == 1 
  88.         && Com_ServerState ())
  89.         Cvar_Set ("paused", "1");
  90.  
  91.     // if this menu is already present, drop back to that level
  92.     // to avoid stacking menus by hotkeys
  93.     for (i=0 ; i<m_menudepth ; i++)
  94.         if (m_layers[i].draw == draw &&
  95.             m_layers[i].key == key)
  96.         {
  97.             m_menudepth = i;
  98.         }
  99.  
  100.     if (i == m_menudepth)
  101.     {
  102.         if (m_menudepth >= MAX_MENU_DEPTH)
  103.             Com_Error (ERR_FATAL, "M_PushMenu: MAX_MENU_DEPTH");
  104.         m_layers[m_menudepth].draw = m_drawfunc;
  105.         m_layers[m_menudepth].key = m_keyfunc;
  106.         m_menudepth++;
  107.     }
  108.  
  109.     m_drawfunc = draw;
  110.     m_keyfunc = key;
  111.  
  112.     m_entersound = true;
  113.  
  114.     cls.key_dest = key_menu;
  115. }
  116.  
  117. void M_ForceMenuOff (void)
  118. {
  119.     m_drawfunc = 0;
  120.     m_keyfunc = 0;
  121.     cls.key_dest = key_game;
  122.     m_menudepth = 0;
  123.     Key_ClearStates ();
  124.     Cvar_Set ("paused", "0");
  125. }
  126.  
  127. void M_PopMenu (void)
  128. {
  129.     S_StartLocalSound( menu_out_sound );
  130.     if (m_menudepth < 1)
  131.         Com_Error (ERR_FATAL, "M_PopMenu: depth < 1");
  132.     m_menudepth--;
  133.  
  134.     m_drawfunc = m_layers[m_menudepth].draw;
  135.     m_keyfunc = m_layers[m_menudepth].key;
  136.  
  137.     if (!m_menudepth)
  138.         M_ForceMenuOff ();
  139. }
  140.  
  141.  
  142. const char *Default_MenuKey( menuframework_s *m, int key )
  143. {
  144.     const char *sound = NULL;
  145.     menucommon_s *item;
  146.  
  147.     if ( m )
  148.     {
  149.         if ( ( item = Menu_ItemAtCursor( m ) ) != 0 )
  150.         {
  151.             if ( item->type == MTYPE_FIELD )
  152.             {
  153.                 if ( Field_Key( ( menufield_s * ) item, key ) )
  154.                     return NULL;
  155.             }
  156.         }
  157.     }
  158.  
  159.     switch ( key )
  160.     {
  161.     case K_ESCAPE:
  162.         M_PopMenu();
  163.         return menu_out_sound;
  164.     case K_KP_UPARROW:
  165.     case K_UPARROW:
  166.         if ( m )
  167.         {
  168.             m->cursor--;
  169.             Menu_AdjustCursor( m, -1 );
  170.             sound = menu_move_sound;
  171.         }
  172.         break;
  173.     case K_TAB:
  174.         if ( m )
  175.         {
  176.             m->cursor++;
  177.             Menu_AdjustCursor( m, 1 );
  178.             sound = menu_move_sound;
  179.         }
  180.         break;
  181.     case K_KP_DOWNARROW:
  182.     case K_DOWNARROW:
  183.         if ( m )
  184.         {
  185.             m->cursor++;
  186.             Menu_AdjustCursor( m, 1 );
  187.             sound = menu_move_sound;
  188.         }
  189.         break;
  190.     case K_KP_LEFTARROW:
  191.     case K_LEFTARROW:
  192.         if ( m )
  193.         {
  194.             Menu_SlideItem( m, -1 );
  195.             sound = menu_move_sound;
  196.         }
  197.         break;
  198.     case K_KP_RIGHTARROW:
  199.     case K_RIGHTARROW:
  200.         if ( m )
  201.         {
  202.             Menu_SlideItem( m, 1 );
  203.             sound = menu_move_sound;
  204.         }
  205.         break;
  206.  
  207.     case K_MOUSE1:
  208.     case K_MOUSE2:
  209.     case K_MOUSE3:
  210.     case K_JOY1:
  211.     case K_JOY2:
  212.     case K_JOY3:
  213.     case K_JOY4:
  214.     case K_AUX1:
  215.     case K_AUX2:
  216.     case K_AUX3:
  217.     case K_AUX4:
  218.     case K_AUX5:
  219.     case K_AUX6:
  220.     case K_AUX7:
  221.     case K_AUX8:
  222.     case K_AUX9:
  223.     case K_AUX10:
  224.     case K_AUX11:
  225.     case K_AUX12:
  226.     case K_AUX13:
  227.     case K_AUX14:
  228.     case K_AUX15:
  229.     case K_AUX16:
  230.     case K_AUX17:
  231.     case K_AUX18:
  232.     case K_AUX19:
  233.     case K_AUX20:
  234.     case K_AUX21:
  235.     case K_AUX22:
  236.     case K_AUX23:
  237.     case K_AUX24:
  238.     case K_AUX25:
  239.     case K_AUX26:
  240.     case K_AUX27:
  241.     case K_AUX28:
  242.     case K_AUX29:
  243.     case K_AUX30:
  244.     case K_AUX31:
  245.     case K_AUX32:
  246.         
  247.     case K_KP_ENTER:
  248.     case K_ENTER:
  249.         if ( m )
  250.             Menu_SelectItem( m );
  251.         sound = menu_move_sound;
  252.         break;
  253.     }
  254.  
  255.     return sound;
  256. }
  257.  
  258. //=============================================================================
  259.  
  260. /*
  261. ================
  262. M_DrawCharacter
  263.  
  264. Draws one solid graphics character
  265. cx and cy are in 320*240 coordinates, and will be centered on
  266. higher res screens.
  267. ================
  268. */
  269. void M_DrawCharacter (int cx, int cy, int num)
  270. {
  271.     Draw_Char ( cx + ((viddef.width - 320)>>1), cy + ((viddef.height - 240)>>1), num);
  272. }
  273.  
  274. void M_Print (int cx, int cy, char *str)
  275. {
  276.     while (*str)
  277.     {
  278.         M_DrawCharacter (cx, cy, (*str)+128);
  279.         str++;
  280.         cx += 8;
  281.     }
  282. }
  283.  
  284. void M_PrintWhite (int cx, int cy, char *str)
  285. {
  286.     while (*str)
  287.     {
  288.         M_DrawCharacter (cx, cy, *str);
  289.         str++;
  290.         cx += 8;
  291.     }
  292. }
  293.  
  294. void M_DrawPic (int x, int y, char *pic)
  295. {
  296.     Draw_Pic (x + ((viddef.width - 320)>>1), y + ((viddef.height - 240)>>1), pic);
  297. }
  298.  
  299.  
  300. /*
  301. =============
  302. M_DrawCursor
  303.  
  304. Draws an animating cursor with the point at
  305. x,y.  The pic will extend to the left of x,
  306. and both above and below y.
  307. =============
  308. */
  309. void M_DrawCursor( int x, int y, int f )
  310. {
  311.     char    cursorname[80];
  312.     static qboolean cached;
  313.  
  314.     if ( !cached )
  315.     {
  316.         int i;
  317.  
  318.         for ( i = 0; i < NUM_CURSOR_FRAMES; i++ )
  319.         {
  320.             Com_sprintf( cursorname, sizeof( cursorname ), "m_cursor%d", i );
  321.  
  322.             R_RegisterPic( cursorname );
  323.         }
  324.         cached = true;
  325.     }
  326.  
  327.     Com_sprintf( cursorname, sizeof(cursorname), "m_cursor%d", f );
  328.     Draw_Pic( x, y, cursorname );
  329. }
  330.  
  331. void M_DrawTextBox (int x, int y, int width, int lines)
  332. {
  333.     int        cx, cy;
  334.     int        n;
  335.  
  336.     // draw left side
  337.     cx = x;
  338.     cy = y;
  339.     M_DrawCharacter (cx, cy, 1);
  340.     for (n = 0; n < lines; n++)
  341.     {
  342.         cy += 8;
  343.         M_DrawCharacter (cx, cy, 4);
  344.     }
  345.     M_DrawCharacter (cx, cy+8, 7);
  346.  
  347.     // draw middle
  348.     cx += 8;
  349.     while (width > 0)
  350.     {
  351.         cy = y;
  352.         M_DrawCharacter (cx, cy, 2);
  353.         for (n = 0; n < lines; n++)
  354.         {
  355.             cy += 8;
  356.             M_DrawCharacter (cx, cy, 5);
  357.         }
  358.         M_DrawCharacter (cx, cy+8, 8);
  359.         width -= 1;
  360.         cx += 8;
  361.     }
  362.  
  363.     // draw right side
  364.     cy = y;
  365.     M_DrawCharacter (cx, cy, 3);
  366.     for (n = 0; n < lines; n++)
  367.     {
  368.         cy += 8;
  369.         M_DrawCharacter (cx, cy, 6);
  370.     }
  371.     M_DrawCharacter (cx, cy+8, 9);
  372. }
  373.  
  374.         
  375. /*
  376. =======================================================================
  377.  
  378. MAIN MENU
  379.  
  380. =======================================================================
  381. */
  382. #define    MAIN_ITEMS    5
  383.  
  384.  
  385. void M_Main_Draw (void)
  386. {
  387.     int i;
  388.     int w, h;
  389.     int ystart;
  390.     int    xoffset;
  391.     int widest = -1;
  392.     int totalheight = 0;
  393.     char litname[80];
  394.     char *names[] =
  395.     {
  396.         "m_main_game",
  397.         "m_main_multiplayer",
  398.         "m_main_options",
  399.         "m_main_video",
  400.         "m_main_quit",
  401.         0
  402.     };
  403.  
  404.     for ( i = 0; names[i] != 0; i++ )
  405.     {
  406.         Draw_GetPicSize( &w, &h, names[i] );
  407.  
  408.         if ( w > widest )
  409.             widest = w;
  410.         totalheight += ( h + 12 );
  411.     }
  412.  
  413.     ystart = ( viddef.height / 2 - 110 );
  414.     xoffset = ( viddef.width - widest + 70 ) / 2;
  415.  
  416.     for ( i = 0; names[i] != 0; i++ )
  417.     {
  418.         if ( i != m_main_cursor )
  419.             Draw_Pic( xoffset, ystart + i * 40 + 13, names[i] );
  420.     }
  421.     strcpy( litname, names[m_main_cursor] );
  422.     strcat( litname, "_sel" );
  423.     Draw_Pic( xoffset, ystart + m_main_cursor * 40 + 13, litname );
  424.  
  425.     M_DrawCursor( xoffset - 25, ystart + m_main_cursor * 40 + 11, (int)(cls.realtime / 100)%NUM_CURSOR_FRAMES );
  426.  
  427.     Draw_GetPicSize( &w, &h, "m_main_plaque" );
  428.     Draw_Pic( xoffset - 30 - w, ystart, "m_main_plaque" );
  429.  
  430.     Draw_Pic( xoffset - 30 - w, ystart + h + 5, "m_main_logo" );
  431. }
  432.  
  433.  
  434. const char *M_Main_Key (int key)
  435. {
  436.     const char *sound = menu_move_sound;
  437.  
  438.     switch (key)
  439.     {
  440.     case K_ESCAPE:
  441.         M_PopMenu ();
  442.         break;
  443.  
  444.     case K_KP_DOWNARROW:
  445.     case K_DOWNARROW:
  446.         if (++m_main_cursor >= MAIN_ITEMS)
  447.             m_main_cursor = 0;
  448.         return sound;
  449.  
  450.     case K_KP_UPARROW:
  451.     case K_UPARROW:
  452.         if (--m_main_cursor < 0)
  453.             m_main_cursor = MAIN_ITEMS - 1;
  454.         return sound;
  455.  
  456.     case K_KP_ENTER:
  457.     case K_ENTER:
  458.         m_entersound = true;
  459.  
  460.         switch (m_main_cursor)
  461.         {
  462.         case 0:
  463.             M_Menu_Game_f ();
  464.             break;
  465.  
  466.         case 1:
  467.             M_Menu_Multiplayer_f();
  468.             break;
  469.  
  470.         case 2:
  471.             M_Menu_Options_f ();
  472.             break;
  473.  
  474.         case 3:
  475.             M_Menu_Video_f ();
  476.             break;
  477.  
  478.         case 4:
  479.             M_Menu_Quit_f ();
  480.             break;
  481.         }
  482.     }
  483.  
  484.     return NULL;
  485. }
  486.  
  487.  
  488. void M_Menu_Main_f (void)
  489. {
  490.     M_PushMenu (M_Main_Draw, M_Main_Key);
  491. }
  492.  
  493. /*
  494. =======================================================================
  495.  
  496. MULTIPLAYER MENU
  497.  
  498. =======================================================================
  499. */
  500. static menuframework_s    s_multiplayer_menu;
  501. static menuaction_s        s_join_network_server_action;
  502. static menuaction_s        s_start_network_server_action;
  503. static menuaction_s        s_player_setup_action;
  504.  
  505. static void Multiplayer_MenuDraw (void)
  506. {
  507.     M_Banner( "m_banner_multiplayer" );
  508.  
  509.     Menu_AdjustCursor( &s_multiplayer_menu, 1 );
  510.     Menu_Draw( &s_multiplayer_menu );
  511. }
  512.  
  513. static void PlayerSetupFunc( void *unused )
  514. {
  515.     M_Menu_PlayerConfig_f();
  516. }
  517.  
  518. static void JoinNetworkServerFunc( void *unused )
  519. {
  520.     M_Menu_JoinServer_f();
  521. }
  522.  
  523. static void StartNetworkServerFunc( void *unused )
  524. {
  525.     M_Menu_StartServer_f ();
  526. }
  527.  
  528. void Multiplayer_MenuInit( void )
  529. {
  530.     s_multiplayer_menu.x = viddef.width * 0.50 - 64;
  531.     s_multiplayer_menu.nitems = 0;
  532.  
  533.     s_join_network_server_action.generic.type    = MTYPE_ACTION;
  534.     s_join_network_server_action.generic.flags  = QMF_LEFT_JUSTIFY;
  535.     s_join_network_server_action.generic.x        = 0;
  536.     s_join_network_server_action.generic.y        = 0;
  537.     s_join_network_server_action.generic.name    = " join network server";
  538.     s_join_network_server_action.generic.callback = JoinNetworkServerFunc;
  539.  
  540.     s_start_network_server_action.generic.type    = MTYPE_ACTION;
  541.     s_start_network_server_action.generic.flags  = QMF_LEFT_JUSTIFY;
  542.     s_start_network_server_action.generic.x        = 0;
  543.     s_start_network_server_action.generic.y        = 10;
  544.     s_start_network_server_action.generic.name    = " start network server";
  545.     s_start_network_server_action.generic.callback = StartNetworkServerFunc;
  546.  
  547.     s_player_setup_action.generic.type    = MTYPE_ACTION;
  548.     s_player_setup_action.generic.flags  = QMF_LEFT_JUSTIFY;
  549.     s_player_setup_action.generic.x        = 0;
  550.     s_player_setup_action.generic.y        = 20;
  551.     s_player_setup_action.generic.name    = " player setup";
  552.     s_player_setup_action.generic.callback = PlayerSetupFunc;
  553.  
  554.     Menu_AddItem( &s_multiplayer_menu, ( void * ) &s_join_network_server_action );
  555.     Menu_AddItem( &s_multiplayer_menu, ( void * ) &s_start_network_server_action );
  556.     Menu_AddItem( &s_multiplayer_menu, ( void * ) &s_player_setup_action );
  557.  
  558.     Menu_SetStatusBar( &s_multiplayer_menu, NULL );
  559.  
  560.     Menu_Center( &s_multiplayer_menu );
  561. }
  562.  
  563. const char *Multiplayer_MenuKey( int key )
  564. {
  565.     return Default_MenuKey( &s_multiplayer_menu, key );
  566. }
  567.  
  568. void M_Menu_Multiplayer_f( void )
  569. {
  570.     Multiplayer_MenuInit();
  571.     M_PushMenu( Multiplayer_MenuDraw, Multiplayer_MenuKey );
  572. }
  573.  
  574. /*
  575. =======================================================================
  576.  
  577. KEYS MENU
  578.  
  579. =======================================================================
  580. */
  581. char *bindnames[][2] =
  582. {
  583. {"+attack",         "attack"},
  584. {"weapnext",         "next weapon"},
  585. {"+forward",         "walk forward"},
  586. {"+back",             "backpedal"},
  587. {"+left",             "turn left"},
  588. {"+right",             "turn right"},
  589. {"+speed",             "run"},
  590. {"+moveleft",         "step left"},
  591. {"+moveright",         "step right"},
  592. {"+strafe",         "sidestep"},
  593. {"+lookup",         "look up"},
  594. {"+lookdown",         "look down"},
  595. {"centerview",         "center view"},
  596. {"+mlook",             "mouse look"},
  597. {"+klook",             "keyboard look"},
  598. {"+moveup",            "up / jump"},
  599. {"+movedown",        "down / crouch"},
  600.  
  601. {"inven",            "inventory"},
  602. {"invuse",            "use item"},
  603. {"invdrop",            "drop item"},
  604. {"invprev",            "prev item"},
  605. {"invnext",            "next item"},
  606.  
  607. {"cmd help",         "help computer" }, 
  608. { 0, 0 }
  609. };
  610.  
  611. int                keys_cursor;
  612. static int        bind_grab;
  613.  
  614. static menuframework_s    s_keys_menu;
  615. static menuaction_s        s_keys_attack_action;
  616. static menuaction_s        s_keys_change_weapon_action;
  617. static menuaction_s        s_keys_walk_forward_action;
  618. static menuaction_s        s_keys_backpedal_action;
  619. static menuaction_s        s_keys_turn_left_action;
  620. static menuaction_s        s_keys_turn_right_action;
  621. static menuaction_s        s_keys_run_action;
  622. static menuaction_s        s_keys_step_left_action;
  623. static menuaction_s        s_keys_step_right_action;
  624. static menuaction_s        s_keys_sidestep_action;
  625. static menuaction_s        s_keys_look_up_action;
  626. static menuaction_s        s_keys_look_down_action;
  627. static menuaction_s        s_keys_center_view_action;
  628. static menuaction_s        s_keys_mouse_look_action;
  629. static menuaction_s        s_keys_keyboard_look_action;
  630. static menuaction_s        s_keys_move_up_action;
  631. static menuaction_s        s_keys_move_down_action;
  632. static menuaction_s        s_keys_inventory_action;
  633. static menuaction_s        s_keys_inv_use_action;
  634. static menuaction_s        s_keys_inv_drop_action;
  635. static menuaction_s        s_keys_inv_prev_action;
  636. static menuaction_s        s_keys_inv_next_action;
  637.  
  638. static menuaction_s        s_keys_help_computer_action;
  639.  
  640. static void M_UnbindCommand (char *command)
  641. {
  642.     int        j;
  643.     int        l;
  644.     char    *b;
  645.  
  646.     l = strlen(command);
  647.  
  648.     for (j=0 ; j<256 ; j++)
  649.     {
  650.         b = keybindings[j];
  651.         if (!b)
  652.             continue;
  653.         if (!strncmp (b, command, l) )
  654.             Key_SetBinding (j, "");
  655.     }
  656. }
  657.  
  658. static void M_FindKeysForCommand (char *command, int *twokeys)
  659. {
  660.     int        count;
  661.     int        j;
  662.     int        l;
  663.     char    *b;
  664.  
  665.     twokeys[0] = twokeys[1] = -1;
  666.     l = strlen(command);
  667.     count = 0;
  668.  
  669.     for (j=0 ; j<256 ; j++)
  670.     {
  671.         b = keybindings[j];
  672.         if (!b)
  673.             continue;
  674.         if (!strncmp (b, command, l) )
  675.         {
  676.             twokeys[count] = j;
  677.             count++;
  678.             if (count == 2)
  679.                 break;
  680.         }
  681.     }
  682. }
  683.  
  684. static void KeyCursorDrawFunc( menuframework_s *menu )
  685. {
  686.     if ( bind_grab )
  687.         Draw_Char( menu->x, menu->y + menu->cursor * 9, '=' );
  688.     else
  689.         Draw_Char( menu->x, menu->y + menu->cursor * 9, 12 + ( ( int ) ( Sys_Milliseconds() / 250 ) & 1 ) );
  690. }
  691.  
  692. static void DrawKeyBindingFunc( void *self )
  693. {
  694.     int keys[2];
  695.     menuaction_s *a = ( menuaction_s * ) self;
  696.  
  697.     M_FindKeysForCommand( bindnames[a->generic.localdata[0]][0], keys);
  698.         
  699.     if (keys[0] == -1)
  700.     {
  701.         Menu_DrawString( a->generic.x + a->generic.parent->x + 16, a->generic.y + a->generic.parent->y, "???" );
  702.     }
  703.     else
  704.     {
  705.         int x;
  706.         const char *name;
  707.  
  708.         name = Key_KeynumToString (keys[0]);
  709.  
  710.         Menu_DrawString( a->generic.x + a->generic.parent->x + 16, a->generic.y + a->generic.parent->y, name );
  711.  
  712.         x = strlen(name) * 8;
  713.  
  714.         if (keys[1] != -1)
  715.         {
  716.             Menu_DrawString( a->generic.x + a->generic.parent->x + 24 + x, a->generic.y + a->generic.parent->y, "or" );
  717.             Menu_DrawString( a->generic.x + a->generic.parent->x + 48 + x, a->generic.y + a->generic.parent->y, Key_KeynumToString (keys[1]) );
  718.         }
  719.     }
  720. }
  721.  
  722. static void KeyBindingFunc( void *self )
  723. {
  724.     menuaction_s *a = ( menuaction_s * ) self;
  725.     int keys[2];
  726.  
  727.     M_FindKeysForCommand( bindnames[a->generic.localdata[0]][0], keys );
  728.  
  729.     if (keys[1] != -1)
  730.         M_UnbindCommand( bindnames[a->generic.localdata[0]][0]);
  731.  
  732.     bind_grab = true;
  733.  
  734.     Menu_SetStatusBar( &s_keys_menu, "press a key or button for this action" );
  735. }
  736.  
  737. static void Keys_MenuInit( void )
  738. {
  739.     int y = 0;
  740.     int i = 0;
  741.  
  742.     s_keys_menu.x = viddef.width * 0.50;
  743.     s_keys_menu.nitems = 0;
  744.     s_keys_menu.cursordraw = KeyCursorDrawFunc;
  745.  
  746.     s_keys_attack_action.generic.type    = MTYPE_ACTION;
  747.     s_keys_attack_action.generic.flags  = QMF_GRAYED;
  748.     s_keys_attack_action.generic.x        = 0;
  749.     s_keys_attack_action.generic.y        = y;
  750.     s_keys_attack_action.generic.ownerdraw = DrawKeyBindingFunc;
  751.     s_keys_attack_action.generic.localdata[0] = i;
  752.     s_keys_attack_action.generic.name    = bindnames[s_keys_attack_action.generic.localdata[0]][1];
  753.  
  754.     s_keys_change_weapon_action.generic.type    = MTYPE_ACTION;
  755.     s_keys_change_weapon_action.generic.flags  = QMF_GRAYED;
  756.     s_keys_change_weapon_action.generic.x        = 0;
  757.     s_keys_change_weapon_action.generic.y        = y += 9;
  758.     s_keys_change_weapon_action.generic.ownerdraw = DrawKeyBindingFunc;
  759.     s_keys_change_weapon_action.generic.localdata[0] = ++i;
  760.     s_keys_change_weapon_action.generic.name    = bindnames[s_keys_change_weapon_action.generic.localdata[0]][1];
  761.  
  762.     s_keys_walk_forward_action.generic.type    = MTYPE_ACTION;
  763.     s_keys_walk_forward_action.generic.flags  = QMF_GRAYED;
  764.     s_keys_walk_forward_action.generic.x        = 0;
  765.     s_keys_walk_forward_action.generic.y        = y += 9;
  766.     s_keys_walk_forward_action.generic.ownerdraw = DrawKeyBindingFunc;
  767.     s_keys_walk_forward_action.generic.localdata[0] = ++i;
  768.     s_keys_walk_forward_action.generic.name    = bindnames[s_keys_walk_forward_action.generic.localdata[0]][1];
  769.  
  770.     s_keys_backpedal_action.generic.type    = MTYPE_ACTION;
  771.     s_keys_backpedal_action.generic.flags  = QMF_GRAYED;
  772.     s_keys_backpedal_action.generic.x        = 0;
  773.     s_keys_backpedal_action.generic.y        = y += 9;
  774.     s_keys_backpedal_action.generic.ownerdraw = DrawKeyBindingFunc;
  775.     s_keys_backpedal_action.generic.localdata[0] = ++i;
  776.     s_keys_backpedal_action.generic.name    = bindnames[s_keys_backpedal_action.generic.localdata[0]][1];
  777.  
  778.     s_keys_turn_left_action.generic.type    = MTYPE_ACTION;
  779.     s_keys_turn_left_action.generic.flags  = QMF_GRAYED;
  780.     s_keys_turn_left_action.generic.x        = 0;
  781.     s_keys_turn_left_action.generic.y        = y += 9;
  782.     s_keys_turn_left_action.generic.ownerdraw = DrawKeyBindingFunc;
  783.     s_keys_turn_left_action.generic.localdata[0] = ++i;
  784.     s_keys_turn_left_action.generic.name    = bindnames[s_keys_turn_left_action.generic.localdata[0]][1];
  785.  
  786.     s_keys_turn_right_action.generic.type    = MTYPE_ACTION;
  787.     s_keys_turn_right_action.generic.flags  = QMF_GRAYED;
  788.     s_keys_turn_right_action.generic.x        = 0;
  789.     s_keys_turn_right_action.generic.y        = y += 9;
  790.     s_keys_turn_right_action.generic.ownerdraw = DrawKeyBindingFunc;
  791.     s_keys_turn_right_action.generic.localdata[0] = ++i;
  792.     s_keys_turn_right_action.generic.name    = bindnames[s_keys_turn_right_action.generic.localdata[0]][1];
  793.  
  794.     s_keys_run_action.generic.type    = MTYPE_ACTION;
  795.     s_keys_run_action.generic.flags  = QMF_GRAYED;
  796.     s_keys_run_action.generic.x        = 0;
  797.     s_keys_run_action.generic.y        = y += 9;
  798.     s_keys_run_action.generic.ownerdraw = DrawKeyBindingFunc;
  799.     s_keys_run_action.generic.localdata[0] = ++i;
  800.     s_keys_run_action.generic.name    = bindnames[s_keys_run_action.generic.localdata[0]][1];
  801.  
  802.     s_keys_step_left_action.generic.type    = MTYPE_ACTION;
  803.     s_keys_step_left_action.generic.flags  = QMF_GRAYED;
  804.     s_keys_step_left_action.generic.x        = 0;
  805.     s_keys_step_left_action.generic.y        = y += 9;
  806.     s_keys_step_left_action.generic.ownerdraw = DrawKeyBindingFunc;
  807.     s_keys_step_left_action.generic.localdata[0] = ++i;
  808.     s_keys_step_left_action.generic.name    = bindnames[s_keys_step_left_action.generic.localdata[0]][1];
  809.  
  810.     s_keys_step_right_action.generic.type    = MTYPE_ACTION;
  811.     s_keys_step_right_action.generic.flags  = QMF_GRAYED;
  812.     s_keys_step_right_action.generic.x        = 0;
  813.     s_keys_step_right_action.generic.y        = y += 9;
  814.     s_keys_step_right_action.generic.ownerdraw = DrawKeyBindingFunc;
  815.     s_keys_step_right_action.generic.localdata[0] = ++i;
  816.     s_keys_step_right_action.generic.name    = bindnames[s_keys_step_right_action.generic.localdata[0]][1];
  817.  
  818.     s_keys_sidestep_action.generic.type    = MTYPE_ACTION;
  819.     s_keys_sidestep_action.generic.flags  = QMF_GRAYED;
  820.     s_keys_sidestep_action.generic.x        = 0;
  821.     s_keys_sidestep_action.generic.y        = y += 9;
  822.     s_keys_sidestep_action.generic.ownerdraw = DrawKeyBindingFunc;
  823.     s_keys_sidestep_action.generic.localdata[0] = ++i;
  824.     s_keys_sidestep_action.generic.name    = bindnames[s_keys_sidestep_action.generic.localdata[0]][1];
  825.  
  826.     s_keys_look_up_action.generic.type    = MTYPE_ACTION;
  827.     s_keys_look_up_action.generic.flags  = QMF_GRAYED;
  828.     s_keys_look_up_action.generic.x        = 0;
  829.     s_keys_look_up_action.generic.y        = y += 9;
  830.     s_keys_look_up_action.generic.ownerdraw = DrawKeyBindingFunc;
  831.     s_keys_look_up_action.generic.localdata[0] = ++i;
  832.     s_keys_look_up_action.generic.name    = bindnames[s_keys_look_up_action.generic.localdata[0]][1];
  833.  
  834.     s_keys_look_down_action.generic.type    = MTYPE_ACTION;
  835.     s_keys_look_down_action.generic.flags  = QMF_GRAYED;
  836.     s_keys_look_down_action.generic.x        = 0;
  837.     s_keys_look_down_action.generic.y        = y += 9;
  838.     s_keys_look_down_action.generic.ownerdraw = DrawKeyBindingFunc;
  839.     s_keys_look_down_action.generic.localdata[0] = ++i;
  840.     s_keys_look_down_action.generic.name    = bindnames[s_keys_look_down_action.generic.localdata[0]][1];
  841.  
  842.     s_keys_center_view_action.generic.type    = MTYPE_ACTION;
  843.     s_keys_center_view_action.generic.flags  = QMF_GRAYED;
  844.     s_keys_center_view_action.generic.x        = 0;
  845.     s_keys_center_view_action.generic.y        = y += 9;
  846.     s_keys_center_view_action.generic.ownerdraw = DrawKeyBindingFunc;
  847.     s_keys_center_view_action.generic.localdata[0] = ++i;
  848.     s_keys_center_view_action.generic.name    = bindnames[s_keys_center_view_action.generic.localdata[0]][1];
  849.  
  850.     s_keys_mouse_look_action.generic.type    = MTYPE_ACTION;
  851.     s_keys_mouse_look_action.generic.flags  = QMF_GRAYED;
  852.     s_keys_mouse_look_action.generic.x        = 0;
  853.     s_keys_mouse_look_action.generic.y        = y += 9;
  854.     s_keys_mouse_look_action.generic.ownerdraw = DrawKeyBindingFunc;
  855.     s_keys_mouse_look_action.generic.localdata[0] = ++i;
  856.     s_keys_mouse_look_action.generic.name    = bindnames[s_keys_mouse_look_action.generic.localdata[0]][1];
  857.  
  858.     s_keys_keyboard_look_action.generic.type    = MTYPE_ACTION;
  859.     s_keys_keyboard_look_action.generic.flags  = QMF_GRAYED;
  860.     s_keys_keyboard_look_action.generic.x        = 0;
  861.     s_keys_keyboard_look_action.generic.y        = y += 9;
  862.     s_keys_keyboard_look_action.generic.ownerdraw = DrawKeyBindingFunc;
  863.     s_keys_keyboard_look_action.generic.localdata[0] = ++i;
  864.     s_keys_keyboard_look_action.generic.name    = bindnames[s_keys_keyboard_look_action.generic.localdata[0]][1];
  865.  
  866.     s_keys_move_up_action.generic.type    = MTYPE_ACTION;
  867.     s_keys_move_up_action.generic.flags  = QMF_GRAYED;
  868.     s_keys_move_up_action.generic.x        = 0;
  869.     s_keys_move_up_action.generic.y        = y += 9;
  870.     s_keys_move_up_action.generic.ownerdraw = DrawKeyBindingFunc;
  871.     s_keys_move_up_action.generic.localdata[0] = ++i;
  872.     s_keys_move_up_action.generic.name    = bindnames[s_keys_move_up_action.generic.localdata[0]][1];
  873.  
  874.     s_keys_move_down_action.generic.type    = MTYPE_ACTION;
  875.     s_keys_move_down_action.generic.flags  = QMF_GRAYED;
  876.     s_keys_move_down_action.generic.x        = 0;
  877.     s_keys_move_down_action.generic.y        = y += 9;
  878.     s_keys_move_down_action.generic.ownerdraw = DrawKeyBindingFunc;
  879.     s_keys_move_down_action.generic.localdata[0] = ++i;
  880.     s_keys_move_down_action.generic.name    = bindnames[s_keys_move_down_action.generic.localdata[0]][1];
  881.  
  882.     s_keys_inventory_action.generic.type    = MTYPE_ACTION;
  883.     s_keys_inventory_action.generic.flags  = QMF_GRAYED;
  884.     s_keys_inventory_action.generic.x        = 0;
  885.     s_keys_inventory_action.generic.y        = y += 9;
  886.     s_keys_inventory_action.generic.ownerdraw = DrawKeyBindingFunc;
  887.     s_keys_inventory_action.generic.localdata[0] = ++i;
  888.     s_keys_inventory_action.generic.name    = bindnames[s_keys_inventory_action.generic.localdata[0]][1];
  889.  
  890.     s_keys_inv_use_action.generic.type    = MTYPE_ACTION;
  891.     s_keys_inv_use_action.generic.flags  = QMF_GRAYED;
  892.     s_keys_inv_use_action.generic.x        = 0;
  893.     s_keys_inv_use_action.generic.y        = y += 9;
  894.     s_keys_inv_use_action.generic.ownerdraw = DrawKeyBindingFunc;
  895.     s_keys_inv_use_action.generic.localdata[0] = ++i;
  896.     s_keys_inv_use_action.generic.name    = bindnames[s_keys_inv_use_action.generic.localdata[0]][1];
  897.  
  898.     s_keys_inv_drop_action.generic.type    = MTYPE_ACTION;
  899.     s_keys_inv_drop_action.generic.flags  = QMF_GRAYED;
  900.     s_keys_inv_drop_action.generic.x        = 0;
  901.     s_keys_inv_drop_action.generic.y        = y += 9;
  902.     s_keys_inv_drop_action.generic.ownerdraw = DrawKeyBindingFunc;
  903.     s_keys_inv_drop_action.generic.localdata[0] = ++i;
  904.     s_keys_inv_drop_action.generic.name    = bindnames[s_keys_inv_drop_action.generic.localdata[0]][1];
  905.  
  906.     s_keys_inv_prev_action.generic.type    = MTYPE_ACTION;
  907.     s_keys_inv_prev_action.generic.flags  = QMF_GRAYED;
  908.     s_keys_inv_prev_action.generic.x        = 0;
  909.     s_keys_inv_prev_action.generic.y        = y += 9;
  910.     s_keys_inv_prev_action.generic.ownerdraw = DrawKeyBindingFunc;
  911.     s_keys_inv_prev_action.generic.localdata[0] = ++i;
  912.     s_keys_inv_prev_action.generic.name    = bindnames[s_keys_inv_prev_action.generic.localdata[0]][1];
  913.  
  914.     s_keys_inv_next_action.generic.type    = MTYPE_ACTION;
  915.     s_keys_inv_next_action.generic.flags  = QMF_GRAYED;
  916.     s_keys_inv_next_action.generic.x        = 0;
  917.     s_keys_inv_next_action.generic.y        = y += 9;
  918.     s_keys_inv_next_action.generic.ownerdraw = DrawKeyBindingFunc;
  919.     s_keys_inv_next_action.generic.localdata[0] = ++i;
  920.     s_keys_inv_next_action.generic.name    = bindnames[s_keys_inv_next_action.generic.localdata[0]][1];
  921.  
  922.     s_keys_help_computer_action.generic.type    = MTYPE_ACTION;
  923.     s_keys_help_computer_action.generic.flags  = QMF_GRAYED;
  924.     s_keys_help_computer_action.generic.x        = 0;
  925.     s_keys_help_computer_action.generic.y        = y += 9;
  926.     s_keys_help_computer_action.generic.ownerdraw = DrawKeyBindingFunc;
  927.     s_keys_help_computer_action.generic.localdata[0] = ++i;
  928.     s_keys_help_computer_action.generic.name    = bindnames[s_keys_help_computer_action.generic.localdata[0]][1];
  929.  
  930.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_attack_action );
  931.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_change_weapon_action );
  932.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_walk_forward_action );
  933.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_backpedal_action );
  934.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_turn_left_action );
  935.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_turn_right_action );
  936.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_run_action );
  937.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_step_left_action );
  938.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_step_right_action );
  939.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_sidestep_action );
  940.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_look_up_action );
  941.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_look_down_action );
  942.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_center_view_action );
  943.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_mouse_look_action );
  944.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_keyboard_look_action );
  945.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_move_up_action );
  946.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_move_down_action );
  947.  
  948.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_inventory_action );
  949.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_inv_use_action );
  950.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_inv_drop_action );
  951.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_inv_prev_action );
  952.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_inv_next_action );
  953.  
  954.     Menu_AddItem( &s_keys_menu, ( void * ) &s_keys_help_computer_action );
  955.     
  956.     Menu_SetStatusBar( &s_keys_menu, "enter to change, backspace to clear" );
  957.     Menu_Center( &s_keys_menu );
  958. }
  959.  
  960. static void Keys_MenuDraw (void)
  961. {
  962.     Menu_AdjustCursor( &s_keys_menu, 1 );
  963.     Menu_Draw( &s_keys_menu );
  964. }
  965.  
  966. static const char *Keys_MenuKey( int key )
  967. {
  968.     menuaction_s *item = ( menuaction_s * ) Menu_ItemAtCursor( &s_keys_menu );
  969.  
  970.     if ( bind_grab )
  971.     {    
  972.         if ( key != K_ESCAPE && key != '`' )
  973.         {
  974.             char cmd[1024];
  975.  
  976.             Com_sprintf (cmd, sizeof(cmd), "bind \"%s\" \"%s\"\n", Key_KeynumToString(key), bindnames[item->generic.localdata[0]][0]);
  977.             Cbuf_InsertText (cmd);
  978.         }
  979.         
  980.         Menu_SetStatusBar( &s_keys_menu, "enter to change, backspace to clear" );
  981.         bind_grab = false;
  982.         return menu_out_sound;
  983.     }
  984.  
  985.     switch ( key )
  986.     {
  987.     case K_KP_ENTER:
  988.     case K_ENTER:
  989.         KeyBindingFunc( item );
  990.         return menu_in_sound;
  991.     case K_BACKSPACE:        // delete bindings
  992.     case K_DEL:                // delete bindings
  993.     case K_KP_DEL:
  994.         M_UnbindCommand( bindnames[item->generic.localdata[0]][0] );
  995.         return menu_out_sound;
  996.     default:
  997.         return Default_MenuKey( &s_keys_menu, key );
  998.     }
  999. }
  1000.  
  1001. void M_Menu_Keys_f (void)
  1002. {
  1003.     Keys_MenuInit();
  1004.     M_PushMenu( Keys_MenuDraw, Keys_MenuKey );
  1005. }
  1006.  
  1007.  
  1008. /*
  1009. =======================================================================
  1010.  
  1011. CONTROLS MENU
  1012.  
  1013. =======================================================================
  1014. */
  1015. static cvar_t *win_noalttab;
  1016. extern cvar_t *in_joystick;
  1017.  
  1018. static menuframework_s    s_options_menu;
  1019. static menuaction_s        s_options_defaults_action;
  1020. static menuaction_s        s_options_customize_options_action;
  1021. static menuslider_s        s_options_sensitivity_slider;
  1022. static menulist_s        s_options_freelook_box;
  1023. static menulist_s        s_options_noalttab_box;
  1024. static menulist_s        s_options_alwaysrun_box;
  1025. static menulist_s        s_options_invertmouse_box;
  1026. static menulist_s        s_options_lookspring_box;
  1027. static menulist_s        s_options_lookstrafe_box;
  1028. static menulist_s        s_options_crosshair_box;
  1029. static menuslider_s        s_options_sfxvolume_slider;
  1030. static menulist_s        s_options_joystick_box;
  1031. static menulist_s        s_options_cdvolume_box;
  1032. static menulist_s        s_options_quality_list;
  1033. static menulist_s        s_options_compatibility_list;
  1034. static menulist_s        s_options_console_action;
  1035.  
  1036. static void CrosshairFunc( void *unused )
  1037. {
  1038.     Cvar_SetValue( "crosshair", s_options_crosshair_box.curvalue );
  1039. }
  1040.  
  1041. static void JoystickFunc( void *unused )
  1042. {
  1043.     Cvar_SetValue( "in_joystick", s_options_joystick_box.curvalue );
  1044. }
  1045.  
  1046. static void CustomizeControlsFunc( void *unused )
  1047. {
  1048.     M_Menu_Keys_f();
  1049. }
  1050.  
  1051. static void AlwaysRunFunc( void *unused )
  1052. {
  1053.     Cvar_SetValue( "cl_run", s_options_alwaysrun_box.curvalue );
  1054. }
  1055.  
  1056. static void FreeLookFunc( void *unused )
  1057. {
  1058.     Cvar_SetValue( "freelook", s_options_freelook_box.curvalue );
  1059. }
  1060.  
  1061. static void MouseSpeedFunc( void *unused )
  1062. {
  1063.     Cvar_SetValue( "sensitivity", s_options_sensitivity_slider.curvalue / 2.0F );
  1064. }
  1065.  
  1066. static void NoAltTabFunc( void *unused )
  1067. {
  1068.     Cvar_SetValue( "win_noalttab", s_options_noalttab_box.curvalue );
  1069. }
  1070.  
  1071. static float ClampCvar( float min, float max, float value )
  1072. {
  1073.     if ( value < min ) return min;
  1074.     if ( value > max ) return max;
  1075.     return value;
  1076. }
  1077.  
  1078. static void ControlsSetMenuItemValues( void )
  1079. {
  1080.     s_options_sfxvolume_slider.curvalue        = Cvar_VariableValue( "s_volume" ) * 10;
  1081.     s_options_cdvolume_box.curvalue         = !Cvar_VariableValue("cd_nocd");
  1082.     s_options_quality_list.curvalue            = !Cvar_VariableValue( "s_loadas8bit" );
  1083.     s_options_sensitivity_slider.curvalue    = ( sensitivity->value ) * 2;
  1084.  
  1085.     Cvar_SetValue( "cl_run", ClampCvar( 0, 1, cl_run->value ) );
  1086.     s_options_alwaysrun_box.curvalue        = cl_run->value;
  1087.  
  1088.     s_options_invertmouse_box.curvalue        = m_pitch->value < 0;
  1089.  
  1090.     Cvar_SetValue( "lookspring", ClampCvar( 0, 1, lookspring->value ) );
  1091.     s_options_lookspring_box.curvalue        = lookspring->value;
  1092.  
  1093.     Cvar_SetValue( "lookstrafe", ClampCvar( 0, 1, lookstrafe->value ) );
  1094.     s_options_lookstrafe_box.curvalue        = lookstrafe->value;
  1095.  
  1096.     Cvar_SetValue( "freelook", ClampCvar( 0, 1, freelook->value ) );
  1097.     s_options_freelook_box.curvalue            = freelook->value;
  1098.  
  1099.     Cvar_SetValue( "crosshair", ClampCvar( 0, 3, crosshair->value ) );
  1100.     s_options_crosshair_box.curvalue        = crosshair->value;
  1101.  
  1102.     Cvar_SetValue( "in_joystick", ClampCvar( 0, 1, in_joystick->value ) );
  1103.     s_options_joystick_box.curvalue        = in_joystick->value;
  1104.  
  1105.     s_options_noalttab_box.curvalue            = win_noalttab->value;
  1106. }
  1107.  
  1108. static void ControlsResetDefaultsFunc( void *unused )
  1109. {
  1110.     Cbuf_AddText ("exec default.cfg\n");
  1111.     Cbuf_Execute();
  1112.  
  1113.     ControlsSetMenuItemValues();
  1114. }
  1115.  
  1116. static void InvertMouseFunc( void *unused )
  1117. {
  1118.     Cvar_SetValue( "m_pitch", -m_pitch->value );
  1119. }
  1120.  
  1121. static void LookspringFunc( void *unused )
  1122. {
  1123.     Cvar_SetValue( "lookspring", !lookspring->value );
  1124. }
  1125.  
  1126. static void LookstrafeFunc( void *unused )
  1127. {
  1128.     Cvar_SetValue( "lookstrafe", !lookstrafe->value );
  1129. }
  1130.  
  1131. static void UpdateVolumeFunc( void *unused )
  1132. {
  1133.     Cvar_SetValue( "s_volume", s_options_sfxvolume_slider.curvalue / 10 );
  1134. }
  1135.  
  1136. static void UpdateCDVolumeFunc( void *unused )
  1137. {
  1138.     Cvar_SetValue( "cd_nocd", !s_options_cdvolume_box.curvalue );
  1139. }
  1140.  
  1141. static void ConsoleFunc( void *unused )
  1142. {
  1143.     /*
  1144.     ** the proper way to do this is probably to have ToggleConsole_f accept a parameter
  1145.     */
  1146.     extern void Key_ClearTyping( void );
  1147.  
  1148.     if ( cl.attractloop )
  1149.     {
  1150.         Cbuf_AddText ("killserver\n");
  1151.         return;
  1152.     }
  1153.  
  1154.     Key_ClearTyping ();
  1155.     Con_ClearNotify ();
  1156.  
  1157.     M_ForceMenuOff ();
  1158.     cls.key_dest = key_console;
  1159. }
  1160.  
  1161. static void UpdateSoundQualityFunc( void *unused )
  1162. {
  1163.     if ( s_options_quality_list.curvalue )
  1164.     {
  1165.         Cvar_SetValue( "s_khz", 22 );
  1166.         Cvar_SetValue( "s_loadas8bit", false );
  1167.     }
  1168.     else
  1169.     {
  1170.         Cvar_SetValue( "s_khz", 11 );
  1171.         Cvar_SetValue( "s_loadas8bit", true );
  1172.     }
  1173.     
  1174.     Cvar_SetValue( "s_primary", s_options_compatibility_list.curvalue );
  1175.  
  1176.     M_DrawTextBox( 8, 120 - 48, 36, 3 );
  1177.     M_Print( 16 + 16, 120 - 48 + 8,  "Restarting the sound system. This" );
  1178.     M_Print( 16 + 16, 120 - 48 + 16, "could take up to a minute, so" );
  1179.     M_Print( 16 + 16, 120 - 48 + 24, "please be patient." );
  1180.  
  1181.     // the text box won't show up unless we do a buffer swap
  1182.     R_EndFrame();
  1183.  
  1184.     CL_Snd_Restart_f();
  1185. }
  1186.  
  1187. void Options_MenuInit( void )
  1188. {
  1189.     static const char *cd_music_items[] =
  1190.     {
  1191.         "disabled",
  1192.         "enabled",
  1193.         0
  1194.     };
  1195.     static const char *quality_items[] =
  1196.     {
  1197.         "low", "high", 0
  1198.     };
  1199.  
  1200.     static const char *compatibility_items[] =
  1201.     {
  1202.         "max compatibility", "max performance", 0
  1203.     };
  1204.  
  1205.     static const char *yesno_names[] =
  1206.     {
  1207.         "no",
  1208.         "yes",
  1209.         0
  1210.     };
  1211.  
  1212.     static const char *crosshair_names[] =
  1213.     {
  1214.         "none",
  1215.         "cross",
  1216.         "dot",
  1217.         "angle",
  1218.         0
  1219.     };
  1220.  
  1221.     win_noalttab = Cvar_Get( "win_noalttab", "0", CVAR_ARCHIVE );
  1222.  
  1223.     /*
  1224.     ** configure controls menu and menu items
  1225.     */
  1226.     s_options_menu.x = viddef.width / 2;
  1227.     s_options_menu.y = viddef.height / 2 - 58;
  1228.     s_options_menu.nitems = 0;
  1229.  
  1230.     s_options_sfxvolume_slider.generic.type    = MTYPE_SLIDER;
  1231.     s_options_sfxvolume_slider.generic.x    = 0;
  1232.     s_options_sfxvolume_slider.generic.y    = 0;
  1233.     s_options_sfxvolume_slider.generic.name    = "effects volume";
  1234.     s_options_sfxvolume_slider.generic.callback    = UpdateVolumeFunc;
  1235.     s_options_sfxvolume_slider.minvalue        = 0;
  1236.     s_options_sfxvolume_slider.maxvalue        = 10;
  1237.     s_options_sfxvolume_slider.curvalue        = Cvar_VariableValue( "s_volume" ) * 10;
  1238.  
  1239.     s_options_cdvolume_box.generic.type    = MTYPE_SPINCONTROL;
  1240.     s_options_cdvolume_box.generic.x        = 0;
  1241.     s_options_cdvolume_box.generic.y        = 10;
  1242.     s_options_cdvolume_box.generic.name    = "CD music";
  1243.     s_options_cdvolume_box.generic.callback    = UpdateCDVolumeFunc;
  1244.     s_options_cdvolume_box.itemnames        = cd_music_items;
  1245.     s_options_cdvolume_box.curvalue         = !Cvar_VariableValue("cd_nocd");
  1246.  
  1247.     s_options_quality_list.generic.type    = MTYPE_SPINCONTROL;
  1248.     s_options_quality_list.generic.x        = 0;
  1249.     s_options_quality_list.generic.y        = 20;;
  1250.     s_options_quality_list.generic.name        = "sound quality";
  1251.     s_options_quality_list.generic.callback = UpdateSoundQualityFunc;
  1252.     s_options_quality_list.itemnames        = quality_items;
  1253.     s_options_quality_list.curvalue            = !Cvar_VariableValue( "s_loadas8bit" );
  1254.  
  1255.     s_options_compatibility_list.generic.type    = MTYPE_SPINCONTROL;
  1256.     s_options_compatibility_list.generic.x        = 0;
  1257.     s_options_compatibility_list.generic.y        = 30;
  1258.     s_options_compatibility_list.generic.name    = "sound compatibility";
  1259.     s_options_compatibility_list.generic.callback = UpdateSoundQualityFunc;
  1260.     s_options_compatibility_list.itemnames        = compatibility_items;
  1261.     s_options_compatibility_list.curvalue        = Cvar_VariableValue( "s_primary" );
  1262.  
  1263.     s_options_sensitivity_slider.generic.type    = MTYPE_SLIDER;
  1264.     s_options_sensitivity_slider.generic.x        = 0;
  1265.     s_options_sensitivity_slider.generic.y        = 50;
  1266.     s_options_sensitivity_slider.generic.name    = "mouse speed";
  1267.     s_options_sensitivity_slider.generic.callback = MouseSpeedFunc;
  1268.     s_options_sensitivity_slider.minvalue        = 2;
  1269.     s_options_sensitivity_slider.maxvalue        = 22;
  1270.  
  1271.     s_options_alwaysrun_box.generic.type = MTYPE_SPINCONTROL;
  1272.     s_options_alwaysrun_box.generic.x    = 0;
  1273.     s_options_alwaysrun_box.generic.y    = 60;
  1274.     s_options_alwaysrun_box.generic.name    = "always run";
  1275.     s_options_alwaysrun_box.generic.callback = AlwaysRunFunc;
  1276.     s_options_alwaysrun_box.itemnames = yesno_names;
  1277.  
  1278.     s_options_invertmouse_box.generic.type = MTYPE_SPINCONTROL;
  1279.     s_options_invertmouse_box.generic.x    = 0;
  1280.     s_options_invertmouse_box.generic.y    = 70;
  1281.     s_options_invertmouse_box.generic.name    = "invert mouse";
  1282.     s_options_invertmouse_box.generic.callback = InvertMouseFunc;
  1283.     s_options_invertmouse_box.itemnames = yesno_names;
  1284.  
  1285.     s_options_lookspring_box.generic.type = MTYPE_SPINCONTROL;
  1286.     s_options_lookspring_box.generic.x    = 0;
  1287.     s_options_lookspring_box.generic.y    = 80;
  1288.     s_options_lookspring_box.generic.name    = "lookspring";
  1289.     s_options_lookspring_box.generic.callback = LookspringFunc;
  1290.     s_options_lookspring_box.itemnames = yesno_names;
  1291.  
  1292.     s_options_lookstrafe_box.generic.type = MTYPE_SPINCONTROL;
  1293.     s_options_lookstrafe_box.generic.x    = 0;
  1294.     s_options_lookstrafe_box.generic.y    = 90;
  1295.     s_options_lookstrafe_box.generic.name    = "lookstrafe";
  1296.     s_options_lookstrafe_box.generic.callback = LookstrafeFunc;
  1297.     s_options_lookstrafe_box.itemnames = yesno_names;
  1298.  
  1299.     s_options_freelook_box.generic.type = MTYPE_SPINCONTROL;
  1300.     s_options_freelook_box.generic.x    = 0;
  1301.     s_options_freelook_box.generic.y    = 100;
  1302.     s_options_freelook_box.generic.name    = "free look";
  1303.     s_options_freelook_box.generic.callback = FreeLookFunc;
  1304.     s_options_freelook_box.itemnames = yesno_names;
  1305.  
  1306.     s_options_crosshair_box.generic.type = MTYPE_SPINCONTROL;
  1307.     s_options_crosshair_box.generic.x    = 0;
  1308.     s_options_crosshair_box.generic.y    = 110;
  1309.     s_options_crosshair_box.generic.name    = "crosshair";
  1310.     s_options_crosshair_box.generic.callback = CrosshairFunc;
  1311.     s_options_crosshair_box.itemnames = crosshair_names;
  1312. /*
  1313.     s_options_noalttab_box.generic.type = MTYPE_SPINCONTROL;
  1314.     s_options_noalttab_box.generic.x    = 0;
  1315.     s_options_noalttab_box.generic.y    = 110;
  1316.     s_options_noalttab_box.generic.name    = "disable alt-tab";
  1317.     s_options_noalttab_box.generic.callback = NoAltTabFunc;
  1318.     s_options_noalttab_box.itemnames = yesno_names;
  1319. */
  1320.     s_options_joystick_box.generic.type = MTYPE_SPINCONTROL;
  1321.     s_options_joystick_box.generic.x    = 0;
  1322.     s_options_joystick_box.generic.y    = 120;
  1323.     s_options_joystick_box.generic.name    = "use joystick";
  1324.     s_options_joystick_box.generic.callback = JoystickFunc;
  1325.     s_options_joystick_box.itemnames = yesno_names;
  1326.  
  1327.     s_options_customize_options_action.generic.type    = MTYPE_ACTION;
  1328.     s_options_customize_options_action.generic.x        = 0;
  1329.     s_options_customize_options_action.generic.y        = 140;
  1330.     s_options_customize_options_action.generic.name    = "customize controls";
  1331.     s_options_customize_options_action.generic.callback = CustomizeControlsFunc;
  1332.  
  1333.     s_options_defaults_action.generic.type    = MTYPE_ACTION;
  1334.     s_options_defaults_action.generic.x        = 0;
  1335.     s_options_defaults_action.generic.y        = 150;
  1336.     s_options_defaults_action.generic.name    = "reset defaults";
  1337.     s_options_defaults_action.generic.callback = ControlsResetDefaultsFunc;
  1338.  
  1339.     s_options_console_action.generic.type    = MTYPE_ACTION;
  1340.     s_options_console_action.generic.x        = 0;
  1341.     s_options_console_action.generic.y        = 160;
  1342.     s_options_console_action.generic.name    = "go to console";
  1343.     s_options_console_action.generic.callback = ConsoleFunc;
  1344.  
  1345.     ControlsSetMenuItemValues();
  1346.  
  1347.     Menu_AddItem( &s_options_menu, ( void * ) &s_options_sfxvolume_slider );
  1348.     Menu_AddItem( &s_options_menu, ( void * ) &s_options_cdvolume_box );
  1349.     Menu_AddItem( &s_options_menu, ( void * ) &s_options_quality_list );
  1350.     Menu_AddItem( &s_options_menu, ( void * ) &s_options_compatibility_list );
  1351.     Menu_AddItem( &s_options_menu, ( void * ) &s_options_sensitivity_slider );
  1352.     Menu_AddItem( &s_options_menu, ( void * ) &s_options_alwaysrun_box );
  1353.     Menu_AddItem( &s_options_menu, ( void * ) &s_options_invertmouse_box );
  1354.     Menu_AddItem( &s_options_menu, ( void * ) &s_options_lookspring_box );
  1355.     Menu_AddItem( &s_options_menu, ( void * ) &s_options_lookstrafe_box );
  1356.     Menu_AddItem( &s_options_menu, ( void * ) &s_options_freelook_box );
  1357.     Menu_AddItem( &s_options_menu, ( void * ) &s_options_crosshair_box );
  1358.     Menu_AddItem( &s_options_menu, ( void * ) &s_options_joystick_box );
  1359.     Menu_AddItem( &s_options_menu, ( void * ) &s_options_customize_options_action );
  1360.     Menu_AddItem( &s_options_menu, ( void * ) &s_options_defaults_action );
  1361.     Menu_AddItem( &s_options_menu, ( void * ) &s_options_console_action );
  1362. }
  1363.  
  1364. void Options_MenuDraw (void)
  1365. {
  1366.     M_Banner( "m_banner_options" );
  1367.     Menu_AdjustCursor( &s_options_menu, 1 );
  1368.     Menu_Draw( &s_options_menu );
  1369. }
  1370.  
  1371. const char *Options_MenuKey( int key )
  1372. {
  1373.     return Default_MenuKey( &s_options_menu, key );
  1374. }
  1375.  
  1376. void M_Menu_Options_f (void)
  1377. {
  1378.     Options_MenuInit();
  1379.     M_PushMenu ( Options_MenuDraw, Options_MenuKey );
  1380. }
  1381.  
  1382. /*
  1383. =======================================================================
  1384.  
  1385. VIDEO MENU
  1386.  
  1387. =======================================================================
  1388. */
  1389.  
  1390. void M_Menu_Video_f (void)
  1391. {
  1392.     VID_MenuInit();
  1393.     M_PushMenu( VID_MenuDraw, VID_MenuKey );
  1394. }
  1395.  
  1396. /*
  1397. =============================================================================
  1398.  
  1399. END GAME MENU
  1400.  
  1401. =============================================================================
  1402. */
  1403. static int credits_start_time;
  1404. static const char **credits;
  1405. static char *creditsIndex[256];
  1406. static char *creditsBuffer;
  1407. static const char *idcredits[] =
  1408. {
  1409.     "+QUAKE II BY ID SOFTWARE",
  1410.     "",
  1411.     "+PROGRAMMING",
  1412.     "John Carmack",
  1413.     "John Cash",
  1414.     "Brian Hook",
  1415.     "",
  1416.     "+ART",
  1417.     "Adrian Carmack",
  1418.     "Kevin Cloud",
  1419.     "Paul Steed",
  1420.     "",
  1421.     "+LEVEL DESIGN",
  1422.     "Tim Willits",
  1423.     "American McGee",
  1424.     "Christian Antkow",
  1425.     "Paul Jaquays",
  1426.     "Brandon James",
  1427.     "",
  1428.     "+BIZ",
  1429.     "Todd Hollenshead",
  1430.     "Barrett (Bear) Alexander",
  1431.     "Donna Jackson",
  1432.     "",
  1433.     "",
  1434.     "+SPECIAL THANKS",
  1435.     "Ben Donges for beta testing",
  1436.     "",
  1437.     "",
  1438.     "",
  1439.     "",
  1440.     "",
  1441.     "",
  1442.     "+ADDITIONAL SUPPORT",
  1443.     "",
  1444.     "+LINUX PORT AND CTF",
  1445.     "Dave \"Zoid\" Kirsch",
  1446.     "",
  1447.     "+CINEMATIC SEQUENCES",
  1448.     "Ending Cinematic by Blur Studio - ",
  1449.     "Venice, CA",
  1450.     "",
  1451.     "Environment models for Introduction",
  1452.     "Cinematic by Karl Dolgener",
  1453.     "",
  1454.     "Assistance with environment design",
  1455.     "by Cliff Iwai",
  1456.     "",
  1457.     "+SOUND EFFECTS AND MUSIC",
  1458.     "Sound Design by Soundelux Media Labs.",
  1459.     "Music Composed and Produced by",
  1460.     "Soundelux Media Labs.  Special thanks",
  1461.     "to Bill Brown, Tom Ozanich, Brian",
  1462.     "Celano, Jeff Eisner, and The Soundelux",
  1463.     "Players.",
  1464.     "",
  1465.     "\"Level Music\" by Sonic Mayhem",
  1466.     "www.sonicmayhem.com",
  1467.     "",
  1468.     "\"Quake II Theme Song\"",
  1469.     "(C) 1997 Rob Zombie. All Rights",
  1470.     "Reserved.",
  1471.     "",
  1472.     "Track 10 (\"Climb\") by Jer Sypult",
  1473.     "",
  1474.     "Voice of computers by",
  1475.     "Carly Staehlin-Taylor",
  1476.     "",
  1477.     "+THANKS TO ACTIVISION",
  1478.     "+IN PARTICULAR:",
  1479.     "",
  1480.     "John Tam",
  1481.     "Steve Rosenthal",
  1482.     "Marty Stratton",
  1483.     "Henk Hartong",
  1484.     "",
  1485.     "Quake II(tm) (C)1997 Id Software, Inc.",
  1486.     "All Rights Reserved.  Distributed by",
  1487.     "Activision, Inc. under license.",
  1488.     "Quake II(tm), the Id Software name,",
  1489.     "the \"Q II\"(tm) logo and id(tm)",
  1490.     "logo are trademarks of Id Software,",
  1491.     "Inc. Activision(R) is a registered",
  1492.     "trademark of Activision, Inc. All",
  1493.     "other trademarks and trade names are",
  1494.     "properties of their respective owners.",
  1495.     0
  1496. };
  1497.  
  1498. static const char *xatcredits[] =
  1499. {
  1500.     "+QUAKE II MISSION PACK: THE RECKONING",
  1501.     "+BY",
  1502.     "+XATRIX ENTERTAINMENT, INC.",
  1503.     "",
  1504.     "+DESIGN AND DIRECTION",
  1505.     "Drew Markham",
  1506.     "",
  1507.     "+PRODUCED BY",
  1508.     "Greg Goodrich",
  1509.     "",
  1510.     "+PROGRAMMING",
  1511.     "Rafael Paiz",
  1512.     "",
  1513.     "+LEVEL DESIGN / ADDITIONAL GAME DESIGN",
  1514.     "Alex Mayberry",
  1515.     "",
  1516.     "+LEVEL DESIGN",
  1517.     "Mal Blackwell",
  1518.     "Dan Koppel",
  1519.     "",
  1520.     "+ART DIRECTION",
  1521.     "Michael \"Maxx\" Kaufman",
  1522.     "",
  1523.     "+COMPUTER GRAPHICS SUPERVISOR AND",
  1524.     "+CHARACTER ANIMATION DIRECTION",
  1525.     "Barry Dempsey",
  1526.     "",
  1527.     "+SENIOR ANIMATOR AND MODELER",
  1528.     "Jason Hoover",
  1529.     "",
  1530.     "+CHARACTER ANIMATION AND",
  1531.     "+MOTION CAPTURE SPECIALIST",
  1532.     "Amit Doron",
  1533.     "",
  1534.     "+ART",
  1535.     "Claire Praderie-Markham",
  1536.     "Viktor Antonov",
  1537.     "Corky Lehmkuhl",
  1538.     "",
  1539.     "+INTRODUCTION ANIMATION",
  1540.     "Dominique Drozdz",
  1541.     "",
  1542.     "+ADDITIONAL LEVEL DESIGN",
  1543.     "Aaron Barber",
  1544.     "Rhett Baldwin",
  1545.     "",
  1546.     "+3D CHARACTER ANIMATION TOOLS",
  1547.     "Gerry Tyra, SA Technology",
  1548.     "",
  1549.     "+ADDITIONAL EDITOR TOOL PROGRAMMING",
  1550.     "Robert Duffy",
  1551.     "",
  1552.     "+ADDITIONAL PROGRAMMING",
  1553.     "Ryan Feltrin",
  1554.     "",
  1555.     "+PRODUCTION COORDINATOR",
  1556.     "Victoria Sylvester",
  1557.     "",
  1558.     "+SOUND DESIGN",
  1559.     "Gary Bradfield",
  1560.     "",
  1561.     "+MUSIC BY",
  1562.     "Sonic Mayhem",
  1563.     "",
  1564.     "",
  1565.     "",
  1566.     "+SPECIAL THANKS",
  1567.     "+TO",
  1568.     "+OUR FRIENDS AT ID SOFTWARE",
  1569.     "",
  1570.     "John Carmack",
  1571.     "John Cash",
  1572.     "Brian Hook",
  1573.     "Adrian Carmack",
  1574.     "Kevin Cloud",
  1575.     "Paul Steed",
  1576.     "Tim Willits",
  1577.     "Christian Antkow",
  1578.     "Paul Jaquays",
  1579.     "Brandon James",
  1580.     "Todd Hollenshead",
  1581.     "Barrett (Bear) Alexander",
  1582.     "Dave \"Zoid\" Kirsch",
  1583.     "Donna Jackson",
  1584.     "",
  1585.     "",
  1586.     "",
  1587.     "+THANKS TO ACTIVISION",
  1588.     "+IN PARTICULAR:",
  1589.     "",
  1590.     "Marty Stratton",
  1591.     "Henk \"The Original Ripper\" Hartong",
  1592.     "Kevin Kraff",
  1593.     "Jamey Gottlieb",
  1594.     "Chris Hepburn",
  1595.     "",
  1596.     "+AND THE GAME TESTERS",
  1597.     "",
  1598.     "Tim Vanlaw",
  1599.     "Doug Jacobs",
  1600.     "Steven Rosenthal",
  1601.     "David Baker",
  1602.     "Chris Campbell",
  1603.     "Aaron Casillas",
  1604.     "Steve Elwell",
  1605.     "Derek Johnstone",
  1606.     "Igor Krinitskiy",
  1607.     "Samantha Lee",
  1608.     "Michael Spann",
  1609.     "Chris Toft",
  1610.     "Juan Valdes",
  1611.     "",
  1612.     "+THANKS TO INTERGRAPH COMPUTER SYTEMS",
  1613.     "+IN PARTICULAR:",
  1614.     "",
  1615.     "Michael T. Nicolaou",
  1616.     "",
  1617.     "",
  1618.     "Quake II Mission Pack: The Reckoning",
  1619.     "(tm) (C)1998 Id Software, Inc. All",
  1620.     "Rights Reserved. Developed by Xatrix",
  1621.     "Entertainment, Inc. for Id Software,",
  1622.     "Inc. Distributed by Activision Inc.",
  1623.     "under license. Quake(R) is a",
  1624.     "registered trademark of Id Software,",
  1625.     "Inc. Quake II Mission Pack: The",
  1626.     "Reckoning(tm), Quake II(tm), the Id",
  1627.     "Software name, the \"Q II\"(tm) logo",
  1628.     "and id(tm) logo are trademarks of Id",
  1629.     "Software, Inc. Activision(R) is a",
  1630.     "registered trademark of Activision,",
  1631.     "Inc. Xatrix(R) is a registered",
  1632.     "trademark of Xatrix Entertainment,",
  1633.     "Inc. All other trademarks and trade",
  1634.     "names are properties of their",
  1635.     "respective owners.",
  1636.     0
  1637. };
  1638.  
  1639. static const char *roguecredits[] =
  1640. {
  1641.     "+QUAKE II MISSION PACK 2: GROUND ZERO",
  1642.     "+BY",
  1643.     "+ROGUE ENTERTAINMENT, INC.",
  1644.     "",
  1645.     "+PRODUCED BY",
  1646.     "Jim Molinets",
  1647.     "",
  1648.     "+PROGRAMMING",
  1649.     "Peter Mack",
  1650.     "Patrick Magruder",
  1651.     "",
  1652.     "+LEVEL DESIGN",
  1653.     "Jim Molinets",
  1654.     "Cameron Lamprecht",
  1655.     "Berenger Fish",
  1656.     "Robert Selitto",
  1657.     "Steve Tietze",
  1658.     "Steve Thoms",
  1659.     "",
  1660.     "+ART DIRECTION",
  1661.     "Rich Fleider",
  1662.     "",
  1663.     "+ART",
  1664.     "Rich Fleider",
  1665.     "Steve Maines",
  1666.     "Won Choi",
  1667.     "",
  1668.     "+ANIMATION SEQUENCES",
  1669.     "Creat Studios",
  1670.     "Steve Maines",
  1671.     "",
  1672.     "+ADDITIONAL LEVEL DESIGN",
  1673.     "Rich Fleider",
  1674.     "Steve Maines",
  1675.     "Peter Mack",
  1676.     "",
  1677.     "+SOUND",
  1678.     "James Grunke",
  1679.     "",
  1680.     "+GROUND ZERO THEME",
  1681.     "+AND",
  1682.     "+MUSIC BY",
  1683.     "Sonic Mayhem",
  1684.     "",
  1685.     "+VWEP MODELS",
  1686.     "Brent \"Hentai\" Dill",
  1687.     "",
  1688.     "",
  1689.     "",
  1690.     "+SPECIAL THANKS",
  1691.     "+TO",
  1692.     "+OUR FRIENDS AT ID SOFTWARE",
  1693.     "",
  1694.     "John Carmack",
  1695.     "John Cash",
  1696.     "Brian Hook",
  1697.     "Adrian Carmack",
  1698.     "Kevin Cloud",
  1699.     "Paul Steed",
  1700.     "Tim Willits",
  1701.     "Christian Antkow",
  1702.     "Paul Jaquays",
  1703.     "Brandon James",
  1704.     "Todd Hollenshead",
  1705.     "Barrett (Bear) Alexander",
  1706.     "Katherine Anna Kang",
  1707.     "Donna Jackson",
  1708.     "Dave \"Zoid\" Kirsch",
  1709.     "",
  1710.     "",
  1711.     "",
  1712.     "+THANKS TO ACTIVISION",
  1713.     "+IN PARTICULAR:",
  1714.     "",
  1715.     "Marty Stratton",
  1716.     "Henk Hartong",
  1717.     "Mitch Lasky",
  1718.     "Steve Rosenthal",
  1719.     "Steve Elwell",
  1720.     "",
  1721.     "+AND THE GAME TESTERS",
  1722.     "",
  1723.     "The Ranger Clan",
  1724.     "Dave \"Zoid\" Kirsch",
  1725.     "Nihilistic Software",
  1726.     "Robert Duffy",
  1727.     "",
  1728.     "And Countless Others",
  1729.     "",
  1730.     "",
  1731.     "",
  1732.     "Quake II Mission Pack 2: Ground Zero",
  1733.     "(tm) (C)1998 Id Software, Inc. All",
  1734.     "Rights Reserved. Developed by Rogue",
  1735.     "Entertainment, Inc. for Id Software,",
  1736.     "Inc. Distributed by Activision Inc.",
  1737.     "under license. Quake(R) is a",
  1738.     "registered trademark of Id Software,",
  1739.     "Inc. Quake II Mission Pack 2: Ground",
  1740.     "Zero(tm), Quake II(tm), the Id",
  1741.     "Software name, the \"Q II\"(tm) logo",
  1742.     "and id(tm) logo are trademarks of Id",
  1743.     "Software, Inc. Activision(R) is a",
  1744.     "registered trademark of Activision,",
  1745.     "Inc. Rogue(R) is a registered",
  1746.     "trademark of Rogue Entertainment,",
  1747.     "Inc. All other trademarks and trade",
  1748.     "names are properties of their",
  1749.     "respective owners.",
  1750.     0
  1751. };
  1752.  
  1753.  
  1754. void M_Credits_MenuDraw( void )
  1755. {
  1756.     int i, y;
  1757.  
  1758.     /*
  1759.     ** draw the credits
  1760.     */
  1761.     for ( i = 0, y = viddef.height - ( ( cls.realtime - credits_start_time ) / 40.0F ); credits[i] && y < viddef.height; y += 10, i++ )
  1762.     {
  1763.         int j, stringoffset = 0;
  1764.         int bold = false;
  1765.  
  1766.         if ( y <= -8 )
  1767.             continue;
  1768.  
  1769.         if ( credits[i][0] == '+' )
  1770.         {
  1771.             bold = true;
  1772.             stringoffset = 1;
  1773.         }
  1774.         else
  1775.         {
  1776.             bold = false;
  1777.             stringoffset = 0;
  1778.         }
  1779.  
  1780.         for ( j = 0; credits[i][j+stringoffset]; j++ )
  1781.         {
  1782.             int x;
  1783.  
  1784.             x = ( viddef.width - strlen( credits[i] ) * 8 - stringoffset * 8 ) / 2 + ( j + stringoffset ) * 8;
  1785.  
  1786.             if ( bold )
  1787.                 Draw_Char( x, y, credits[i][j+stringoffset] + 128 );
  1788.             else
  1789.                 Draw_Char( x, y, credits[i][j+stringoffset] );
  1790.         }
  1791.     }
  1792.  
  1793.     if ( y < 0 )
  1794.         credits_start_time = cls.realtime;
  1795. }
  1796.  
  1797. const char *M_Credits_Key( int key )
  1798. {
  1799.     switch (key)
  1800.     {
  1801.     case K_ESCAPE:
  1802.         if (creditsBuffer)
  1803.             FS_FreeFile (creditsBuffer);
  1804.         M_PopMenu ();
  1805.         break;
  1806.     }
  1807.  
  1808.     return menu_out_sound;
  1809.  
  1810. }
  1811.  
  1812. extern int Developer_searchpath (int who);
  1813.  
  1814. void M_Menu_Credits_f( void )
  1815. {
  1816.     int        n;
  1817.     int        count;
  1818.     char    *p;
  1819.     int        isdeveloper = 0;
  1820.  
  1821.     creditsBuffer = NULL;
  1822.     count = FS_LoadFile ("credits", &creditsBuffer);
  1823.     if (count != -1)
  1824.     {
  1825.         p = creditsBuffer;
  1826.         for (n = 0; n < 255; n++)
  1827.         {
  1828.             creditsIndex[n] = p;
  1829.             while (*p != '\r' && *p != '\n')
  1830.             {
  1831.                 p++;
  1832.                 if (--count == 0)
  1833.                     break;
  1834.             }
  1835.             if (*p == '\r')
  1836.             {
  1837.                 *p++ = 0;
  1838.                 if (--count == 0)
  1839.                     break;
  1840.             }
  1841.             *p++ = 0;
  1842.             if (--count == 0)
  1843.                 break;
  1844.         }
  1845.         creditsIndex[++n] = 0;
  1846.         credits = creditsIndex;
  1847.     }
  1848.     else
  1849.     {
  1850.         isdeveloper = Developer_searchpath (1);
  1851.         
  1852.         if (isdeveloper == 1)            // xatrix
  1853.             credits = xatcredits;
  1854.         else if (isdeveloper == 2)        // ROGUE
  1855.             credits = roguecredits;
  1856.         else
  1857.         {
  1858.             credits = idcredits;    
  1859.         }
  1860.  
  1861.     }
  1862.  
  1863.     credits_start_time = cls.realtime;
  1864.     M_PushMenu( M_Credits_MenuDraw, M_Credits_Key);
  1865. }
  1866.  
  1867. /*
  1868. =============================================================================
  1869.  
  1870. GAME MENU
  1871.  
  1872. =============================================================================
  1873. */
  1874.  
  1875. static int        m_game_cursor;
  1876.  
  1877. static menuframework_s    s_game_menu;
  1878. static menuaction_s        s_easy_game_action;
  1879. static menuaction_s        s_medium_game_action;
  1880. static menuaction_s        s_hard_game_action;
  1881. static menuaction_s        s_load_game_action;
  1882. static menuaction_s        s_save_game_action;
  1883. static menuaction_s        s_credits_action;
  1884. static menuseparator_s    s_blankline;
  1885.  
  1886. static void StartGame( void )
  1887. {
  1888.     // disable updates and start the cinematic going
  1889.     cl.servercount = -1;
  1890.     M_ForceMenuOff ();
  1891.     Cvar_SetValue( "deathmatch", 0 );
  1892.     Cvar_SetValue( "coop", 0 );
  1893.  
  1894.     Cvar_SetValue( "gamerules", 0 );        //PGM
  1895.  
  1896.     Cbuf_AddText ("loading ; killserver ; wait ; newgame\n");
  1897.     cls.key_dest = key_game;
  1898. }
  1899.  
  1900. static void EasyGameFunc( void *data )
  1901. {
  1902.     Cvar_ForceSet( "skill", "0" );
  1903.     StartGame();
  1904. }
  1905.  
  1906. static void MediumGameFunc( void *data )
  1907. {
  1908.     Cvar_ForceSet( "skill", "1" );
  1909.     StartGame();
  1910. }
  1911.  
  1912. static void HardGameFunc( void *data )
  1913. {
  1914.     Cvar_ForceSet( "skill", "2" );
  1915.     StartGame();
  1916. }
  1917.  
  1918. static void LoadGameFunc( void *unused )
  1919. {
  1920.     M_Menu_LoadGame_f ();
  1921. }
  1922.  
  1923. static void SaveGameFunc( void *unused )
  1924. {
  1925.     M_Menu_SaveGame_f();
  1926. }
  1927.  
  1928. static void CreditsFunc( void *unused )
  1929. {
  1930.     M_Menu_Credits_f();
  1931. }
  1932.  
  1933. void Game_MenuInit( void )
  1934. {
  1935.     static const char *difficulty_names[] =
  1936.     {
  1937.         "easy",
  1938.         "medium",
  1939.         "hard",
  1940.         0
  1941.     };
  1942.  
  1943.     s_game_menu.x = viddef.width * 0.50;
  1944.     s_game_menu.nitems = 0;
  1945.  
  1946.     s_easy_game_action.generic.type    = MTYPE_ACTION;
  1947.     s_easy_game_action.generic.flags  = QMF_LEFT_JUSTIFY;
  1948.     s_easy_game_action.generic.x        = 0;
  1949.     s_easy_game_action.generic.y        = 0;
  1950.     s_easy_game_action.generic.name    = "easy";
  1951.     s_easy_game_action.generic.callback = EasyGameFunc;
  1952.  
  1953.     s_medium_game_action.generic.type    = MTYPE_ACTION;
  1954.     s_medium_game_action.generic.flags  = QMF_LEFT_JUSTIFY;
  1955.     s_medium_game_action.generic.x        = 0;
  1956.     s_medium_game_action.generic.y        = 10;
  1957.     s_medium_game_action.generic.name    = "medium";
  1958.     s_medium_game_action.generic.callback = MediumGameFunc;
  1959.  
  1960.     s_hard_game_action.generic.type    = MTYPE_ACTION;
  1961.     s_hard_game_action.generic.flags  = QMF_LEFT_JUSTIFY;
  1962.     s_hard_game_action.generic.x        = 0;
  1963.     s_hard_game_action.generic.y        = 20;
  1964.     s_hard_game_action.generic.name    = "hard";
  1965.     s_hard_game_action.generic.callback = HardGameFunc;
  1966.  
  1967.     s_blankline.generic.type = MTYPE_SEPARATOR;
  1968.  
  1969.     s_load_game_action.generic.type    = MTYPE_ACTION;
  1970.     s_load_game_action.generic.flags  = QMF_LEFT_JUSTIFY;
  1971.     s_load_game_action.generic.x        = 0;
  1972.     s_load_game_action.generic.y        = 40;
  1973.     s_load_game_action.generic.name    = "load game";
  1974.     s_load_game_action.generic.callback = LoadGameFunc;
  1975.  
  1976.     s_save_game_action.generic.type    = MTYPE_ACTION;
  1977.     s_save_game_action.generic.flags  = QMF_LEFT_JUSTIFY;
  1978.     s_save_game_action.generic.x        = 0;
  1979.     s_save_game_action.generic.y        = 50;
  1980.     s_save_game_action.generic.name    = "save game";
  1981.     s_save_game_action.generic.callback = SaveGameFunc;
  1982.  
  1983.     s_credits_action.generic.type    = MTYPE_ACTION;
  1984.     s_credits_action.generic.flags  = QMF_LEFT_JUSTIFY;
  1985.     s_credits_action.generic.x        = 0;
  1986.     s_credits_action.generic.y        = 60;
  1987.     s_credits_action.generic.name    = "credits";
  1988.     s_credits_action.generic.callback = CreditsFunc;
  1989.  
  1990.     Menu_AddItem( &s_game_menu, ( void * ) &s_easy_game_action );
  1991.     Menu_AddItem( &s_game_menu, ( void * ) &s_medium_game_action );
  1992.     Menu_AddItem( &s_game_menu, ( void * ) &s_hard_game_action );
  1993.     Menu_AddItem( &s_game_menu, ( void * ) &s_blankline );
  1994.     Menu_AddItem( &s_game_menu, ( void * ) &s_load_game_action );
  1995.     Menu_AddItem( &s_game_menu, ( void * ) &s_save_game_action );
  1996.     Menu_AddItem( &s_game_menu, ( void * ) &s_blankline );
  1997.     Menu_AddItem( &s_game_menu, ( void * ) &s_credits_action );
  1998.  
  1999.     Menu_Center( &s_game_menu );
  2000. }
  2001.  
  2002. void Game_MenuDraw( void )
  2003. {
  2004.     M_Banner( "m_banner_game" );
  2005.     Menu_AdjustCursor( &s_game_menu, 1 );
  2006.     Menu_Draw( &s_game_menu );
  2007. }
  2008.  
  2009. const char *Game_MenuKey( int key )
  2010. {
  2011.     return Default_MenuKey( &s_game_menu, key );
  2012. }
  2013.  
  2014. void M_Menu_Game_f (void)
  2015. {
  2016.     Game_MenuInit();
  2017.     M_PushMenu( Game_MenuDraw, Game_MenuKey );
  2018.     m_game_cursor = 1;
  2019. }
  2020.  
  2021. /*
  2022. =============================================================================
  2023.  
  2024. LOADGAME MENU
  2025.  
  2026. =============================================================================
  2027. */
  2028.  
  2029. #define    MAX_SAVEGAMES    15
  2030.  
  2031. static menuframework_s    s_savegame_menu;
  2032.  
  2033. static menuframework_s    s_loadgame_menu;
  2034. static menuaction_s        s_loadgame_actions[MAX_SAVEGAMES];
  2035.  
  2036. char        m_savestrings[MAX_SAVEGAMES][32];
  2037. qboolean    m_savevalid[MAX_SAVEGAMES];
  2038.  
  2039. void Create_Savestrings (void)
  2040. {
  2041.     int        i;
  2042.     FILE    *f;
  2043.     char    name[MAX_OSPATH];
  2044.  
  2045.     for (i=0 ; i<MAX_SAVEGAMES ; i++)
  2046.     {
  2047.         Com_sprintf (name, sizeof(name), "%s/save/save%i/server.ssv", FS_Gamedir(), i);
  2048.         f = fopen (name, "rb");
  2049.         if (!f)
  2050.         {
  2051.             strcpy (m_savestrings[i], "<EMPTY>");
  2052.             m_savevalid[i] = false;
  2053.         }
  2054.         else
  2055.         {
  2056.             FS_Read (m_savestrings[i], sizeof(m_savestrings[i]), f);
  2057.             fclose (f);
  2058.             m_savevalid[i] = true;
  2059.         }
  2060.     }
  2061. }
  2062.  
  2063. void LoadGameCallback( void *self )
  2064. {
  2065.     menuaction_s *a = ( menuaction_s * ) self;
  2066.  
  2067.     if ( m_savevalid[ a->generic.localdata[0] ] )
  2068.         Cbuf_AddText (va("load save%i\n",  a->generic.localdata[0] ) );
  2069.     M_ForceMenuOff ();
  2070. }
  2071.  
  2072. void LoadGame_MenuInit( void )
  2073. {
  2074.     int i;
  2075.  
  2076.     s_loadgame_menu.x = viddef.width / 2 - 120;
  2077.     s_loadgame_menu.y = viddef.height / 2 - 58;
  2078.     s_loadgame_menu.nitems = 0;
  2079.  
  2080.     Create_Savestrings();
  2081.  
  2082.     for ( i = 0; i < MAX_SAVEGAMES; i++ )
  2083.     {
  2084.         s_loadgame_actions[i].generic.name            = m_savestrings[i];
  2085.         s_loadgame_actions[i].generic.flags            = QMF_LEFT_JUSTIFY;
  2086.         s_loadgame_actions[i].generic.localdata[0]    = i;
  2087.         s_loadgame_actions[i].generic.callback        = LoadGameCallback;
  2088.  
  2089.         s_loadgame_actions[i].generic.x = 0;
  2090.         s_loadgame_actions[i].generic.y = ( i ) * 10;
  2091.         if (i>0)    // separate from autosave
  2092.             s_loadgame_actions[i].generic.y += 10;
  2093.  
  2094.         s_loadgame_actions[i].generic.type = MTYPE_ACTION;
  2095.  
  2096.         Menu_AddItem( &s_loadgame_menu, &s_loadgame_actions[i] );
  2097.     }
  2098. }
  2099.  
  2100. void LoadGame_MenuDraw( void )
  2101. {
  2102.     M_Banner( "m_banner_load_game" );
  2103. //    Menu_AdjustCursor( &s_loadgame_menu, 1 );
  2104.     Menu_Draw( &s_loadgame_menu );
  2105. }
  2106.  
  2107. const char *LoadGame_MenuKey( int key )
  2108. {
  2109.     if ( key == K_ESCAPE || key == K_ENTER )
  2110.     {
  2111.         s_savegame_menu.cursor = s_loadgame_menu.cursor - 1;
  2112.         if ( s_savegame_menu.cursor < 0 )
  2113.             s_savegame_menu.cursor = 0;
  2114.     }
  2115.     return Default_MenuKey( &s_loadgame_menu, key );
  2116. }
  2117.  
  2118. void M_Menu_LoadGame_f (void)
  2119. {
  2120.     LoadGame_MenuInit();
  2121.     M_PushMenu( LoadGame_MenuDraw, LoadGame_MenuKey );
  2122. }
  2123.  
  2124.  
  2125. /*
  2126. =============================================================================
  2127.  
  2128. SAVEGAME MENU
  2129.  
  2130. =============================================================================
  2131. */
  2132. static menuframework_s    s_savegame_menu;
  2133. static menuaction_s        s_savegame_actions[MAX_SAVEGAMES];
  2134.  
  2135. void SaveGameCallback( void *self )
  2136. {
  2137.     menuaction_s *a = ( menuaction_s * ) self;
  2138.  
  2139.     Cbuf_AddText (va("save save%i\n", a->generic.localdata[0] ));
  2140.     M_ForceMenuOff ();
  2141. }
  2142.  
  2143. void SaveGame_MenuDraw( void )
  2144. {
  2145.     M_Banner( "m_banner_save_game" );
  2146.     Menu_AdjustCursor( &s_savegame_menu, 1 );
  2147.     Menu_Draw( &s_savegame_menu );
  2148. }
  2149.  
  2150. void SaveGame_MenuInit( void )
  2151. {
  2152.     int i;
  2153.  
  2154.     s_savegame_menu.x = viddef.width / 2 - 120;
  2155.     s_savegame_menu.y = viddef.height / 2 - 58;
  2156.     s_savegame_menu.nitems = 0;
  2157.  
  2158.     Create_Savestrings();
  2159.  
  2160.     // don't include the autosave slot
  2161.     for ( i = 0; i < MAX_SAVEGAMES-1; i++ )
  2162.     {
  2163.         s_savegame_actions[i].generic.name = m_savestrings[i+1];
  2164.         s_savegame_actions[i].generic.localdata[0] = i+1;
  2165.         s_savegame_actions[i].generic.flags = QMF_LEFT_JUSTIFY;
  2166.         s_savegame_actions[i].generic.callback = SaveGameCallback;
  2167.  
  2168.         s_savegame_actions[i].generic.x = 0;
  2169.         s_savegame_actions[i].generic.y = ( i ) * 10;
  2170.  
  2171.         s_savegame_actions[i].generic.type = MTYPE_ACTION;
  2172.  
  2173.         Menu_AddItem( &s_savegame_menu, &s_savegame_actions[i] );
  2174.     }
  2175. }
  2176.  
  2177. const char *SaveGame_MenuKey( int key )
  2178. {
  2179.     if ( key == K_ENTER || key == K_ESCAPE )
  2180.     {
  2181.         s_loadgame_menu.cursor = s_savegame_menu.cursor - 1;
  2182.         if ( s_loadgame_menu.cursor < 0 )
  2183.             s_loadgame_menu.cursor = 0;
  2184.     }
  2185.     return Default_MenuKey( &s_savegame_menu, key );
  2186. }
  2187.  
  2188. void M_Menu_SaveGame_f (void)
  2189. {
  2190.     if (!Com_ServerState())
  2191.         return;        // not playing a game
  2192.  
  2193.     SaveGame_MenuInit();
  2194.     M_PushMenu( SaveGame_MenuDraw, SaveGame_MenuKey );
  2195.     Create_Savestrings ();
  2196. }
  2197.  
  2198.  
  2199. /*
  2200. =============================================================================
  2201.  
  2202. JOIN SERVER MENU
  2203.  
  2204. =============================================================================
  2205. */
  2206. #define MAX_LOCAL_SERVERS 8
  2207.  
  2208. static menuframework_s    s_joinserver_menu;
  2209. static menuseparator_s    s_joinserver_server_title;
  2210. static menuaction_s        s_joinserver_search_action;
  2211. static menuaction_s        s_joinserver_address_book_action;
  2212. static menuaction_s        s_joinserver_server_actions[MAX_LOCAL_SERVERS];
  2213.  
  2214. int        m_num_servers;
  2215. #define    NO_SERVER_STRING    "<no server>"
  2216.  
  2217. // user readable information
  2218. static char local_server_names[MAX_LOCAL_SERVERS][80];
  2219.  
  2220. // network address
  2221. static netadr_t local_server_netadr[MAX_LOCAL_SERVERS];
  2222.  
  2223. void M_AddToServerList (netadr_t adr, char *info)
  2224. {
  2225.     int        i;
  2226.  
  2227.     if (m_num_servers == MAX_LOCAL_SERVERS)
  2228.         return;
  2229.     while ( *info == ' ' )
  2230.         info++;
  2231.  
  2232.     // ignore if duplicated
  2233.     for (i=0 ; i<m_num_servers ; i++)
  2234.         if (!strcmp(info, local_server_names[i]))
  2235.             return;
  2236.  
  2237.     local_server_netadr[m_num_servers] = adr;
  2238.     strncpy (local_server_names[m_num_servers], info, sizeof(local_server_names[0])-1);
  2239.     m_num_servers++;
  2240. }
  2241.  
  2242.  
  2243. void JoinServerFunc( void *self )
  2244. {
  2245.     char    buffer[128];
  2246.     int        index;
  2247.  
  2248.     index = ( menuaction_s * ) self - s_joinserver_server_actions;
  2249.  
  2250.     if ( Q_stricmp( local_server_names[index], NO_SERVER_STRING ) == 0 )
  2251.         return;
  2252.  
  2253.     if (index >= m_num_servers)
  2254.         return;
  2255.  
  2256.     Com_sprintf (buffer, sizeof(buffer), "connect %s\n", NET_AdrToString (local_server_netadr[index]));
  2257.     Cbuf_AddText (buffer);
  2258.     M_ForceMenuOff ();
  2259. }
  2260.  
  2261. void AddressBookFunc( void *self )
  2262. {
  2263.     M_Menu_AddressBook_f();
  2264. }
  2265.  
  2266. void NullCursorDraw( void *self )
  2267. {
  2268. }
  2269.  
  2270. void SearchLocalGames( void )
  2271. {
  2272.     int        i;
  2273.  
  2274.     m_num_servers = 0;
  2275.     for (i=0 ; i<MAX_LOCAL_SERVERS ; i++)
  2276.         strcpy (local_server_names[i], NO_SERVER_STRING);
  2277.  
  2278.     M_DrawTextBox( 8, 120 - 48, 36, 3 );
  2279.     M_Print( 16 + 16, 120 - 48 + 8,  "Searching for local servers, this" );
  2280.     M_Print( 16 + 16, 120 - 48 + 16, "could take up to a minute, so" );
  2281.     M_Print( 16 + 16, 120 - 48 + 24, "please be patient." );
  2282.  
  2283.     // the text box won't show up unless we do a buffer swap
  2284.     R_EndFrame();
  2285.  
  2286.     // send out info packets
  2287.     CL_PingServers_f();
  2288. }
  2289.  
  2290. void SearchLocalGamesFunc( void *self )
  2291. {
  2292.     SearchLocalGames();
  2293. }
  2294.  
  2295. void JoinServer_MenuInit( void )
  2296. {
  2297.     int i;
  2298.  
  2299.     s_joinserver_menu.x = viddef.width * 0.50 - 120;
  2300.     s_joinserver_menu.nitems = 0;
  2301.  
  2302.     s_joinserver_address_book_action.generic.type    = MTYPE_ACTION;
  2303.     s_joinserver_address_book_action.generic.name    = "address book";
  2304.     s_joinserver_address_book_action.generic.flags    = QMF_LEFT_JUSTIFY;
  2305.     s_joinserver_address_book_action.generic.x        = 0;
  2306.     s_joinserver_address_book_action.generic.y        = 0;
  2307.     s_joinserver_address_book_action.generic.callback = AddressBookFunc;
  2308.  
  2309.     s_joinserver_search_action.generic.type = MTYPE_ACTION;
  2310.     s_joinserver_search_action.generic.name    = "refresh server list";
  2311.     s_joinserver_search_action.generic.flags    = QMF_LEFT_JUSTIFY;
  2312.     s_joinserver_search_action.generic.x    = 0;
  2313.     s_joinserver_search_action.generic.y    = 10;
  2314.     s_joinserver_search_action.generic.callback = SearchLocalGamesFunc;
  2315.     s_joinserver_search_action.generic.statusbar = "search for servers";
  2316.  
  2317.     s_joinserver_server_title.generic.type = MTYPE_SEPARATOR;
  2318.     s_joinserver_server_title.generic.name = "connect to...";
  2319.     s_joinserver_server_title.generic.x    = 80;
  2320.     s_joinserver_server_title.generic.y       = 30;
  2321.  
  2322.     for ( i = 0; i < MAX_LOCAL_SERVERS; i++ )
  2323.     {
  2324.         s_joinserver_server_actions[i].generic.type    = MTYPE_ACTION;
  2325.         strcpy (local_server_names[i], NO_SERVER_STRING);
  2326.         s_joinserver_server_actions[i].generic.name    = local_server_names[i];
  2327.         s_joinserver_server_actions[i].generic.flags    = QMF_LEFT_JUSTIFY;
  2328.         s_joinserver_server_actions[i].generic.x        = 0;
  2329.         s_joinserver_server_actions[i].generic.y        = 40 + i*10;
  2330.         s_joinserver_server_actions[i].generic.callback = JoinServerFunc;
  2331.         s_joinserver_server_actions[i].generic.statusbar = "press ENTER to connect";
  2332.     }
  2333.  
  2334.     Menu_AddItem( &s_joinserver_menu, &s_joinserver_address_book_action );
  2335.     Menu_AddItem( &s_joinserver_menu, &s_joinserver_server_title );
  2336.     Menu_AddItem( &s_joinserver_menu, &s_joinserver_search_action );
  2337.  
  2338.     for ( i = 0; i < 8; i++ )
  2339.         Menu_AddItem( &s_joinserver_menu, &s_joinserver_server_actions[i] );
  2340.  
  2341.     Menu_Center( &s_joinserver_menu );
  2342.  
  2343.     SearchLocalGames();
  2344. }
  2345.  
  2346. void JoinServer_MenuDraw(void)
  2347. {
  2348.     M_Banner( "m_banner_join_server" );
  2349.     Menu_Draw( &s_joinserver_menu );
  2350. }
  2351.  
  2352.  
  2353. const char *JoinServer_MenuKey( int key )
  2354. {
  2355.     return Default_MenuKey( &s_joinserver_menu, key );
  2356. }
  2357.  
  2358. void M_Menu_JoinServer_f (void)
  2359. {
  2360.     JoinServer_MenuInit();
  2361.     M_PushMenu( JoinServer_MenuDraw, JoinServer_MenuKey );
  2362. }
  2363.  
  2364.  
  2365. /*
  2366. =============================================================================
  2367.  
  2368. START SERVER MENU
  2369.  
  2370. =============================================================================
  2371. */
  2372. static menuframework_s s_startserver_menu;
  2373. static char **mapnames;
  2374. static int      nummaps;
  2375.  
  2376. static menuaction_s    s_startserver_start_action;
  2377. static menuaction_s    s_startserver_dmoptions_action;
  2378. static menufield_s    s_timelimit_field;
  2379. static menufield_s    s_fraglimit_field;
  2380. static menufield_s    s_maxclients_field;
  2381. static menufield_s    s_hostname_field;
  2382. static menulist_s    s_startmap_list;
  2383. static menulist_s    s_rules_box;
  2384.  
  2385. void DMOptionsFunc( void *self )
  2386. {
  2387.     if (s_rules_box.curvalue == 1)
  2388.         return;
  2389.     M_Menu_DMOptions_f();
  2390. }
  2391.  
  2392. void RulesChangeFunc ( void *self )
  2393. {
  2394.     // DM
  2395.     if (s_rules_box.curvalue == 0)
  2396.     {
  2397.         s_maxclients_field.generic.statusbar = NULL;
  2398.         s_startserver_dmoptions_action.generic.statusbar = NULL;
  2399.     }
  2400.     else if(s_rules_box.curvalue == 1)        // coop                // PGM
  2401.     {
  2402.         s_maxclients_field.generic.statusbar = "4 maximum for cooperative";
  2403.         if (atoi(s_maxclients_field.buffer) > 4)
  2404.             strcpy( s_maxclients_field.buffer, "4" );
  2405.         s_startserver_dmoptions_action.generic.statusbar = "N/A for cooperative";
  2406.     }
  2407. //=====
  2408. //PGM
  2409.     // ROGUE GAMES
  2410.     else if(Developer_searchpath(2) == 2)
  2411.     {
  2412.         if (s_rules_box.curvalue == 2)            // tag    
  2413.         {
  2414.             s_maxclients_field.generic.statusbar = NULL;
  2415.             s_startserver_dmoptions_action.generic.statusbar = NULL;
  2416.         }
  2417. /*
  2418.         else if(s_rules_box.curvalue == 3)        // deathball
  2419.         {
  2420.             s_maxclients_field.generic.statusbar = NULL;
  2421.             s_startserver_dmoptions_action.generic.statusbar = NULL;
  2422.         }
  2423. */
  2424.     }
  2425. //PGM
  2426. //=====
  2427. }
  2428.  
  2429. void StartServerActionFunc( void *self )
  2430. {
  2431.     char    startmap[1024];
  2432.     int        timelimit;
  2433.     int        fraglimit;
  2434.     int        maxclients;
  2435.     char    *spot;
  2436.  
  2437.     strcpy( startmap, strchr( mapnames[s_startmap_list.curvalue], '\n' ) + 1 );
  2438.  
  2439.     maxclients  = atoi( s_maxclients_field.buffer );
  2440.     timelimit    = atoi( s_timelimit_field.buffer );
  2441.     fraglimit    = atoi( s_fraglimit_field.buffer );
  2442.  
  2443.     Cvar_SetValue( "maxclients", ClampCvar( 0, maxclients, maxclients ) );
  2444.     Cvar_SetValue ("timelimit", ClampCvar( 0, timelimit, timelimit ) );
  2445.     Cvar_SetValue ("fraglimit", ClampCvar( 0, fraglimit, fraglimit ) );
  2446.     Cvar_Set("hostname", s_hostname_field.buffer );
  2447. //    Cvar_SetValue ("deathmatch", !s_rules_box.curvalue );
  2448. //    Cvar_SetValue ("coop", s_rules_box.curvalue );
  2449.  
  2450. //PGM
  2451.     if((s_rules_box.curvalue < 2) || (Developer_searchpath(2) != 2))
  2452.     {
  2453.         Cvar_SetValue ("deathmatch", !s_rules_box.curvalue );
  2454.         Cvar_SetValue ("coop", s_rules_box.curvalue );
  2455.         Cvar_SetValue ("gamerules", 0 );
  2456.     }
  2457.     else
  2458.     {
  2459.         Cvar_SetValue ("deathmatch", 1 );    // deathmatch is always true for rogue games, right?
  2460.         Cvar_SetValue ("coop", 0 );            // FIXME - this might need to depend on which game we're running
  2461.         Cvar_SetValue ("gamerules", s_rules_box.curvalue );
  2462.     }
  2463. //PGM
  2464.  
  2465.     spot = NULL;
  2466.     if (s_rules_box.curvalue == 1)        // PGM
  2467.     {
  2468.          if(Q_stricmp(startmap, "bunk1") == 0)
  2469.               spot = "start";
  2470.          else if(Q_stricmp(startmap, "mintro") == 0)
  2471.               spot = "start";
  2472.          else if(Q_stricmp(startmap, "fact1") == 0)
  2473.               spot = "start";
  2474.          else if(Q_stricmp(startmap, "power1") == 0)
  2475.               spot = "pstart";
  2476.          else if(Q_stricmp(startmap, "biggun") == 0)
  2477.               spot = "bstart";
  2478.          else if(Q_stricmp(startmap, "hangar1") == 0)
  2479.               spot = "unitstart";
  2480.          else if(Q_stricmp(startmap, "city1") == 0)
  2481.               spot = "unitstart";
  2482.          else if(Q_stricmp(startmap, "boss1") == 0)
  2483.             spot = "bosstart";
  2484.     }
  2485.  
  2486.     if (spot)
  2487.     {
  2488.         if (Com_ServerState())
  2489.             Cbuf_AddText ("disconnect\n");
  2490.         Cbuf_AddText (va("gamemap \"*%s$%s\"\n", startmap, spot));
  2491.     }
  2492.     else
  2493.     {
  2494.         Cbuf_AddText (va("map %s\n", startmap));
  2495.     }
  2496.  
  2497.     M_ForceMenuOff ();
  2498. }
  2499.  
  2500. void StartServer_MenuInit( void )
  2501. {
  2502.     static const char *dm_coop_names[] =
  2503.     {
  2504.         "deathmatch",
  2505.         "cooperative",
  2506.         0
  2507.     };
  2508. //=======
  2509. //PGM
  2510.     static const char *dm_coop_names_rogue[] =
  2511.     {
  2512.         "deathmatch",
  2513.         "cooperative",
  2514.         "tag",
  2515. //        "deathball",
  2516.         0
  2517.     };
  2518. //PGM
  2519. //=======
  2520.     char *buffer;
  2521.     char  mapsname[1024];
  2522.     char *s;
  2523.     int length;
  2524.     int i;
  2525.     FILE *fp;
  2526.  
  2527.     /*
  2528.     ** load the list of map names
  2529.     */
  2530.     Com_sprintf( mapsname, sizeof( mapsname ), "%s/maps.lst", FS_Gamedir() );
  2531.     if ( ( fp = fopen( mapsname, "rb" ) ) == 0 )
  2532.     {
  2533.         if ( ( length = FS_LoadFile( "maps.lst", ( void ** ) &buffer ) ) == -1 )
  2534.             Com_Error( ERR_DROP, "couldn't find maps.lst\n" );
  2535.     }
  2536.     else
  2537.     {
  2538. #ifdef _WIN32
  2539.         length = filelength( fileno( fp  ) );
  2540. #else
  2541.         fseek(fp, 0, SEEK_END);
  2542.         length = ftell(fp);
  2543.         fseek(fp, 0, SEEK_SET);
  2544. #endif
  2545.         buffer = malloc( length );
  2546.         fread( buffer, length, 1, fp );
  2547.     }
  2548.  
  2549.     s = buffer;
  2550.  
  2551.     i = 0;
  2552.     while ( i < length )
  2553.     {
  2554.         if ( s[i] == '\r' )
  2555.             nummaps++;
  2556.         i++;
  2557.     }
  2558.  
  2559.     if ( nummaps == 0 )
  2560.         Com_Error( ERR_DROP, "no maps in maps.lst\n" );
  2561.  
  2562.     mapnames = malloc( sizeof( char * ) * ( nummaps + 1 ) );
  2563.     memset( mapnames, 0, sizeof( char * ) * ( nummaps + 1 ) );
  2564.  
  2565.     s = buffer;
  2566.  
  2567.     for ( i = 0; i < nummaps; i++ )
  2568.     {
  2569.     char  shortname[MAX_TOKEN_CHARS];
  2570.     char  longname[MAX_TOKEN_CHARS];
  2571.         char  scratch[200];
  2572.         int        j, l;
  2573.  
  2574.         strcpy( shortname, COM_Parse( &s ) );
  2575.         l = strlen(shortname);
  2576.         for (j=0 ; j<l ; j++)
  2577.             shortname[j] = toupper(shortname[j]);
  2578.         strcpy( longname, COM_Parse( &s ) );
  2579.         Com_sprintf( scratch, sizeof( scratch ), "%s\n%s", longname, shortname );
  2580.  
  2581.         mapnames[i] = malloc( strlen( scratch ) + 1 );
  2582.         strcpy( mapnames[i], scratch );
  2583.     }
  2584.     mapnames[nummaps] = 0;
  2585.  
  2586.     if ( fp != 0 )
  2587.     {
  2588.         fp = 0;
  2589.         free( buffer );
  2590.     }
  2591.     else
  2592.     {
  2593.         FS_FreeFile( buffer );
  2594.     }
  2595.  
  2596.     /*
  2597.     ** initialize the menu stuff
  2598.     */
  2599.     s_startserver_menu.x = viddef.width * 0.50;
  2600.     s_startserver_menu.nitems = 0;
  2601.  
  2602.     s_startmap_list.generic.type = MTYPE_SPINCONTROL;
  2603.     s_startmap_list.generic.x    = 0;
  2604.     s_startmap_list.generic.y    = 0;
  2605.     s_startmap_list.generic.name    = "initial map";
  2606.     s_startmap_list.itemnames = mapnames;
  2607.  
  2608.     s_rules_box.generic.type = MTYPE_SPINCONTROL;
  2609.     s_rules_box.generic.x    = 0;
  2610.     s_rules_box.generic.y    = 20;
  2611.     s_rules_box.generic.name    = "rules";
  2612.     
  2613. //PGM - rogue games only available with rogue DLL.
  2614.     if(Developer_searchpath(2) == 2)
  2615.         s_rules_box.itemnames = dm_coop_names_rogue;
  2616.     else
  2617.         s_rules_box.itemnames = dm_coop_names;
  2618. //PGM
  2619.  
  2620.     if (Cvar_VariableValue("coop"))
  2621.         s_rules_box.curvalue = 1;
  2622.     else
  2623.         s_rules_box.curvalue = 0;
  2624.     s_rules_box.generic.callback = RulesChangeFunc;
  2625.  
  2626.     s_timelimit_field.generic.type = MTYPE_FIELD;
  2627.     s_timelimit_field.generic.name = "time limit";
  2628.     s_timelimit_field.generic.flags = QMF_NUMBERSONLY;
  2629.     s_timelimit_field.generic.x    = 0;
  2630.     s_timelimit_field.generic.y    = 36;
  2631.     s_timelimit_field.generic.statusbar = "0 = no limit";
  2632.     s_timelimit_field.length = 3;
  2633.     s_timelimit_field.visible_length = 3;
  2634.     strcpy( s_timelimit_field.buffer, Cvar_VariableString("timelimit") );
  2635.  
  2636.     s_fraglimit_field.generic.type = MTYPE_FIELD;
  2637.     s_fraglimit_field.generic.name = "frag limit";
  2638.     s_fraglimit_field.generic.flags = QMF_NUMBERSONLY;
  2639.     s_fraglimit_field.generic.x    = 0;
  2640.     s_fraglimit_field.generic.y    = 54;
  2641.     s_fraglimit_field.generic.statusbar = "0 = no limit";
  2642.     s_fraglimit_field.length = 3;
  2643.     s_fraglimit_field.visible_length = 3;
  2644.     strcpy( s_fraglimit_field.buffer, Cvar_VariableString("fraglimit") );
  2645.  
  2646.     /*
  2647.     ** maxclients determines the maximum number of players that can join
  2648.     ** the game.  If maxclients is only "1" then we should default the menu
  2649.     ** option to 8 players, otherwise use whatever its current value is. 
  2650.     ** Clamping will be done when the server is actually started.
  2651.     */
  2652.     s_maxclients_field.generic.type = MTYPE_FIELD;
  2653.     s_maxclients_field.generic.name = "max players";
  2654.     s_maxclients_field.generic.flags = QMF_NUMBERSONLY;
  2655.     s_maxclients_field.generic.x    = 0;
  2656.     s_maxclients_field.generic.y    = 72;
  2657.     s_maxclients_field.generic.statusbar = NULL;
  2658.     s_maxclients_field.length = 3;
  2659.     s_maxclients_field.visible_length = 3;
  2660.     if ( Cvar_VariableValue( "maxclients" ) == 1 )
  2661.         strcpy( s_maxclients_field.buffer, "8" );
  2662.     else 
  2663.         strcpy( s_maxclients_field.buffer, Cvar_VariableString("maxclients") );
  2664.  
  2665.     s_hostname_field.generic.type = MTYPE_FIELD;
  2666.     s_hostname_field.generic.name = "hostname";
  2667.     s_hostname_field.generic.flags = 0;
  2668.     s_hostname_field.generic.x    = 0;
  2669.     s_hostname_field.generic.y    = 90;
  2670.     s_hostname_field.generic.statusbar = NULL;
  2671.     s_hostname_field.length = 12;
  2672.     s_hostname_field.visible_length = 12;
  2673.     strcpy( s_hostname_field.buffer, Cvar_VariableString("hostname") );
  2674.  
  2675.     s_startserver_dmoptions_action.generic.type = MTYPE_ACTION;
  2676.     s_startserver_dmoptions_action.generic.name    = " deathmatch flags";
  2677.     s_startserver_dmoptions_action.generic.flags= QMF_LEFT_JUSTIFY;
  2678.     s_startserver_dmoptions_action.generic.x    = 24;
  2679.     s_startserver_dmoptions_action.generic.y    = 108;
  2680.     s_startserver_dmoptions_action.generic.statusbar = NULL;
  2681.     s_startserver_dmoptions_action.generic.callback = DMOptionsFunc;
  2682.  
  2683.     s_startserver_start_action.generic.type = MTYPE_ACTION;
  2684.     s_startserver_start_action.generic.name    = " begin";
  2685.     s_startserver_start_action.generic.flags= QMF_LEFT_JUSTIFY;
  2686.     s_startserver_start_action.generic.x    = 24;
  2687.     s_startserver_start_action.generic.y    = 128;
  2688.     s_startserver_start_action.generic.callback = StartServerActionFunc;
  2689.  
  2690.     Menu_AddItem( &s_startserver_menu, &s_startmap_list );
  2691.     Menu_AddItem( &s_startserver_menu, &s_rules_box );
  2692.     Menu_AddItem( &s_startserver_menu, &s_timelimit_field );
  2693.     Menu_AddItem( &s_startserver_menu, &s_fraglimit_field );
  2694.     Menu_AddItem( &s_startserver_menu, &s_maxclients_field );
  2695.     Menu_AddItem( &s_startserver_menu, &s_hostname_field );
  2696.     Menu_AddItem( &s_startserver_menu, &s_startserver_dmoptions_action );
  2697.     Menu_AddItem( &s_startserver_menu, &s_startserver_start_action );
  2698.  
  2699.     Menu_Center( &s_startserver_menu );
  2700.  
  2701.     // call this now to set proper inital state
  2702.     RulesChangeFunc ( NULL );
  2703. }
  2704.  
  2705. void StartServer_MenuDraw(void)
  2706. {
  2707.     Menu_Draw( &s_startserver_menu );
  2708. }
  2709.  
  2710. const char *StartServer_MenuKey( int key )
  2711. {
  2712.     if ( key == K_ESCAPE )
  2713.     {
  2714.         if ( mapnames )
  2715.         {
  2716.             int i;
  2717.  
  2718.             for ( i = 0; i < nummaps; i++ )
  2719.                 free( mapnames[i] );
  2720.             free( mapnames );
  2721.         }
  2722.         mapnames = 0;
  2723.         nummaps = 0;
  2724.     }
  2725.  
  2726.     return Default_MenuKey( &s_startserver_menu, key );
  2727. }
  2728.  
  2729. void M_Menu_StartServer_f (void)
  2730. {
  2731.     StartServer_MenuInit();
  2732.     M_PushMenu( StartServer_MenuDraw, StartServer_MenuKey );
  2733. }
  2734.  
  2735. /*
  2736. =============================================================================
  2737.  
  2738. DMOPTIONS BOOK MENU
  2739.  
  2740. =============================================================================
  2741. */
  2742. static char dmoptions_statusbar[128];
  2743.  
  2744. static menuframework_s s_dmoptions_menu;
  2745.  
  2746. static menulist_s    s_friendlyfire_box;
  2747. static menulist_s    s_falls_box;
  2748. static menulist_s    s_weapons_stay_box;
  2749. static menulist_s    s_instant_powerups_box;
  2750. static menulist_s    s_powerups_box;
  2751. static menulist_s    s_health_box;
  2752. static menulist_s    s_spawn_farthest_box;
  2753. static menulist_s    s_teamplay_box;
  2754. static menulist_s    s_samelevel_box;
  2755. static menulist_s    s_force_respawn_box;
  2756. static menulist_s    s_armor_box;
  2757. static menulist_s    s_allow_exit_box;
  2758. static menulist_s    s_infinite_ammo_box;
  2759. static menulist_s    s_fixed_fov_box;
  2760. static menulist_s    s_quad_drop_box;
  2761.  
  2762. //ROGUE
  2763. static menulist_s    s_no_mines_box;
  2764. static menulist_s    s_no_nukes_box;
  2765. static menulist_s    s_stack_double_box;
  2766. static menulist_s    s_no_spheres_box;
  2767. //ROGUE
  2768.  
  2769. static void DMFlagCallback( void *self )
  2770. {
  2771.     menulist_s *f = ( menulist_s * ) self;
  2772.     int flags;
  2773.     int bit = 0;
  2774.  
  2775.     flags = Cvar_VariableValue( "dmflags" );
  2776.  
  2777.     if ( f == &s_friendlyfire_box )
  2778.     {
  2779.         if ( f->curvalue )
  2780.             flags &= ~DF_NO_FRIENDLY_FIRE;
  2781.         else
  2782.             flags |= DF_NO_FRIENDLY_FIRE;
  2783.         goto setvalue;
  2784.     }
  2785.     else if ( f == &s_falls_box )
  2786.     {
  2787.         if ( f->curvalue )
  2788.             flags &= ~DF_NO_FALLING;
  2789.         else
  2790.             flags |= DF_NO_FALLING;
  2791.         goto setvalue;
  2792.     }
  2793.     else if ( f == &s_weapons_stay_box ) 
  2794.     {
  2795.         bit = DF_WEAPONS_STAY;
  2796.     }
  2797.     else if ( f == &s_instant_powerups_box )
  2798.     {
  2799.         bit = DF_INSTANT_ITEMS;
  2800.     }
  2801.     else if ( f == &s_allow_exit_box )
  2802.     {
  2803.         bit = DF_ALLOW_EXIT;
  2804.     }
  2805.     else if ( f == &s_powerups_box )
  2806.     {
  2807.         if ( f->curvalue )
  2808.             flags &= ~DF_NO_ITEMS;
  2809.         else
  2810.             flags |= DF_NO_ITEMS;
  2811.         goto setvalue;
  2812.     }
  2813.     else if ( f == &s_health_box )
  2814.     {
  2815.         if ( f->curvalue )
  2816.             flags &= ~DF_NO_HEALTH;
  2817.         else
  2818.             flags |= DF_NO_HEALTH;
  2819.         goto setvalue;
  2820.     }
  2821.     else if ( f == &s_spawn_farthest_box )
  2822.     {
  2823.         bit = DF_SPAWN_FARTHEST;
  2824.     }
  2825.     else if ( f == &s_teamplay_box )
  2826.     {
  2827.         if ( f->curvalue == 1 )
  2828.         {
  2829.             flags |=  DF_SKINTEAMS;
  2830.             flags &= ~DF_MODELTEAMS;
  2831.         }
  2832.         else if ( f->curvalue == 2 )
  2833.         {
  2834.             flags |=  DF_MODELTEAMS;
  2835.             flags &= ~DF_SKINTEAMS;
  2836.         }
  2837.         else
  2838.         {
  2839.             flags &= ~( DF_MODELTEAMS | DF_SKINTEAMS );
  2840.         }
  2841.  
  2842.         goto setvalue;
  2843.     }
  2844.     else if ( f == &s_samelevel_box )
  2845.     {
  2846.         bit = DF_SAME_LEVEL;
  2847.     }
  2848.     else if ( f == &s_force_respawn_box )
  2849.     {
  2850.         bit = DF_FORCE_RESPAWN;
  2851.     }
  2852.     else if ( f == &s_armor_box )
  2853.     {
  2854.         if ( f->curvalue )
  2855.             flags &= ~DF_NO_ARMOR;
  2856.         else
  2857.             flags |= DF_NO_ARMOR;
  2858.         goto setvalue;
  2859.     }
  2860.     else if ( f == &s_infinite_ammo_box )
  2861.     {
  2862.         bit = DF_INFINITE_AMMO;
  2863.     }
  2864.     else if ( f == &s_fixed_fov_box )
  2865.     {
  2866.         bit = DF_FIXED_FOV;
  2867.     }
  2868.     else if ( f == &s_quad_drop_box )
  2869.     {
  2870.         bit = DF_QUAD_DROP;
  2871.     }
  2872.  
  2873. //=======
  2874. //ROGUE
  2875.     else if (Developer_searchpath(2) == 2)
  2876.     {
  2877.         if ( f == &s_no_mines_box)
  2878.         {
  2879.             bit = DF_NO_MINES;
  2880.         }
  2881.         else if ( f == &s_no_nukes_box)
  2882.         {
  2883.             bit = DF_NO_NUKES;
  2884.         }
  2885.         else if ( f == &s_stack_double_box)
  2886.         {
  2887.             bit = DF_NO_STACK_DOUBLE;
  2888.         }
  2889.         else if ( f == &s_no_spheres_box)
  2890.         {
  2891.             bit = DF_NO_SPHERES;
  2892.         }
  2893.     }
  2894. //ROGUE
  2895. //=======
  2896.  
  2897.     if ( f )
  2898.     {
  2899.         if ( f->curvalue == 0 )
  2900.             flags &= ~bit;
  2901.         else
  2902.             flags |= bit;
  2903.     }
  2904.  
  2905. setvalue:
  2906.     Cvar_SetValue ("dmflags", flags);
  2907.  
  2908.     Com_sprintf( dmoptions_statusbar, sizeof( dmoptions_statusbar ), "dmflags = %d", flags );
  2909.  
  2910. }
  2911.  
  2912. void DMOptions_MenuInit( void )
  2913. {
  2914.     static const char *yes_no_names[] =
  2915.     {
  2916.         "no", "yes", 0
  2917.     };
  2918.     static const char *teamplay_names[] = 
  2919.     {
  2920.         "disabled", "by skin", "by model", 0
  2921.     };
  2922.     int dmflags = Cvar_VariableValue( "dmflags" );
  2923.     int y = 0;
  2924.  
  2925.     s_dmoptions_menu.x = viddef.width * 0.50;
  2926.     s_dmoptions_menu.nitems = 0;
  2927.  
  2928.     s_falls_box.generic.type = MTYPE_SPINCONTROL;
  2929.     s_falls_box.generic.x    = 0;
  2930.     s_falls_box.generic.y    = y;
  2931.     s_falls_box.generic.name    = "falling damage";
  2932.     s_falls_box.generic.callback = DMFlagCallback;
  2933.     s_falls_box.itemnames = yes_no_names;
  2934.     s_falls_box.curvalue = ( dmflags & DF_NO_FALLING ) == 0;
  2935.  
  2936.     s_weapons_stay_box.generic.type = MTYPE_SPINCONTROL;
  2937.     s_weapons_stay_box.generic.x    = 0;
  2938.     s_weapons_stay_box.generic.y    = y += 10;
  2939.     s_weapons_stay_box.generic.name    = "weapons stay";
  2940.     s_weapons_stay_box.generic.callback = DMFlagCallback;
  2941.     s_weapons_stay_box.itemnames = yes_no_names;
  2942.     s_weapons_stay_box.curvalue = ( dmflags & DF_WEAPONS_STAY ) != 0;
  2943.  
  2944.     s_instant_powerups_box.generic.type = MTYPE_SPINCONTROL;
  2945.     s_instant_powerups_box.generic.x    = 0;
  2946.     s_instant_powerups_box.generic.y    = y += 10;
  2947.     s_instant_powerups_box.generic.name    = "instant powerups";
  2948.     s_instant_powerups_box.generic.callback = DMFlagCallback;
  2949.     s_instant_powerups_box.itemnames = yes_no_names;
  2950.     s_instant_powerups_box.curvalue = ( dmflags & DF_INSTANT_ITEMS ) != 0;
  2951.  
  2952.     s_powerups_box.generic.type = MTYPE_SPINCONTROL;
  2953.     s_powerups_box.generic.x    = 0;
  2954.     s_powerups_box.generic.y    = y += 10;
  2955.     s_powerups_box.generic.name    = "allow powerups";
  2956.     s_powerups_box.generic.callback = DMFlagCallback;
  2957.     s_powerups_box.itemnames = yes_no_names;
  2958.     s_powerups_box.curvalue = ( dmflags & DF_NO_ITEMS ) == 0;
  2959.  
  2960.     s_health_box.generic.type = MTYPE_SPINCONTROL;
  2961.     s_health_box.generic.x    = 0;
  2962.     s_health_box.generic.y    = y += 10;
  2963.     s_health_box.generic.callback = DMFlagCallback;
  2964.     s_health_box.generic.name    = "allow health";
  2965.     s_health_box.itemnames = yes_no_names;
  2966.     s_health_box.curvalue = ( dmflags & DF_NO_HEALTH ) == 0;
  2967.  
  2968.     s_armor_box.generic.type = MTYPE_SPINCONTROL;
  2969.     s_armor_box.generic.x    = 0;
  2970.     s_armor_box.generic.y    = y += 10;
  2971.     s_armor_box.generic.name    = "allow armor";
  2972.     s_armor_box.generic.callback = DMFlagCallback;
  2973.     s_armor_box.itemnames = yes_no_names;
  2974.     s_armor_box.curvalue = ( dmflags & DF_NO_ARMOR ) == 0;
  2975.  
  2976.     s_spawn_farthest_box.generic.type = MTYPE_SPINCONTROL;
  2977.     s_spawn_farthest_box.generic.x    = 0;
  2978.     s_spawn_farthest_box.generic.y    = y += 10;
  2979.     s_spawn_farthest_box.generic.name    = "spawn farthest";
  2980.     s_spawn_farthest_box.generic.callback = DMFlagCallback;
  2981.     s_spawn_farthest_box.itemnames = yes_no_names;
  2982.     s_spawn_farthest_box.curvalue = ( dmflags & DF_SPAWN_FARTHEST ) != 0;
  2983.  
  2984.     s_samelevel_box.generic.type = MTYPE_SPINCONTROL;
  2985.     s_samelevel_box.generic.x    = 0;
  2986.     s_samelevel_box.generic.y    = y += 10;
  2987.     s_samelevel_box.generic.name    = "same map";
  2988.     s_samelevel_box.generic.callback = DMFlagCallback;
  2989.     s_samelevel_box.itemnames = yes_no_names;
  2990.     s_samelevel_box.curvalue = ( dmflags & DF_SAME_LEVEL ) != 0;
  2991.  
  2992.     s_force_respawn_box.generic.type = MTYPE_SPINCONTROL;
  2993.     s_force_respawn_box.generic.x    = 0;
  2994.     s_force_respawn_box.generic.y    = y += 10;
  2995.     s_force_respawn_box.generic.name    = "force respawn";
  2996.     s_force_respawn_box.generic.callback = DMFlagCallback;
  2997.     s_force_respawn_box.itemnames = yes_no_names;
  2998.     s_force_respawn_box.curvalue = ( dmflags & DF_FORCE_RESPAWN ) != 0;
  2999.  
  3000.     s_teamplay_box.generic.type = MTYPE_SPINCONTROL;
  3001.     s_teamplay_box.generic.x    = 0;
  3002.     s_teamplay_box.generic.y    = y += 10;
  3003.     s_teamplay_box.generic.name    = "teamplay";
  3004.     s_teamplay_box.generic.callback = DMFlagCallback;
  3005.     s_teamplay_box.itemnames = teamplay_names;
  3006.  
  3007.     s_allow_exit_box.generic.type = MTYPE_SPINCONTROL;
  3008.     s_allow_exit_box.generic.x    = 0;
  3009.     s_allow_exit_box.generic.y    = y += 10;
  3010.     s_allow_exit_box.generic.name    = "allow exit";
  3011.     s_allow_exit_box.generic.callback = DMFlagCallback;
  3012.     s_allow_exit_box.itemnames = yes_no_names;
  3013.     s_allow_exit_box.curvalue = ( dmflags & DF_ALLOW_EXIT ) != 0;
  3014.  
  3015.     s_infinite_ammo_box.generic.type = MTYPE_SPINCONTROL;
  3016.     s_infinite_ammo_box.generic.x    = 0;
  3017.     s_infinite_ammo_box.generic.y    = y += 10;
  3018.     s_infinite_ammo_box.generic.name    = "infinite ammo";
  3019.     s_infinite_ammo_box.generic.callback = DMFlagCallback;
  3020.     s_infinite_ammo_box.itemnames = yes_no_names;
  3021.     s_infinite_ammo_box.curvalue = ( dmflags & DF_INFINITE_AMMO ) != 0;
  3022.  
  3023.     s_fixed_fov_box.generic.type = MTYPE_SPINCONTROL;
  3024.     s_fixed_fov_box.generic.x    = 0;
  3025.     s_fixed_fov_box.generic.y    = y += 10;
  3026.     s_fixed_fov_box.generic.name    = "fixed FOV";
  3027.     s_fixed_fov_box.generic.callback = DMFlagCallback;
  3028.     s_fixed_fov_box.itemnames = yes_no_names;
  3029.     s_fixed_fov_box.curvalue = ( dmflags & DF_FIXED_FOV ) != 0;
  3030.  
  3031.     s_quad_drop_box.generic.type = MTYPE_SPINCONTROL;
  3032.     s_quad_drop_box.generic.x    = 0;
  3033.     s_quad_drop_box.generic.y    = y += 10;
  3034.     s_quad_drop_box.generic.name    = "quad drop";
  3035.     s_quad_drop_box.generic.callback = DMFlagCallback;
  3036.     s_quad_drop_box.itemnames = yes_no_names;
  3037.     s_quad_drop_box.curvalue = ( dmflags & DF_QUAD_DROP ) != 0;
  3038.  
  3039.     s_friendlyfire_box.generic.type = MTYPE_SPINCONTROL;
  3040.     s_friendlyfire_box.generic.x    = 0;
  3041.     s_friendlyfire_box.generic.y    = y += 10;
  3042.     s_friendlyfire_box.generic.name    = "friendly fire";
  3043.     s_friendlyfire_box.generic.callback = DMFlagCallback;
  3044.     s_friendlyfire_box.itemnames = yes_no_names;
  3045.     s_friendlyfire_box.curvalue = ( dmflags & DF_NO_FRIENDLY_FIRE ) == 0;
  3046.  
  3047. //============
  3048. //ROGUE
  3049.     if(Developer_searchpath(2) == 2)
  3050.     {
  3051.         s_no_mines_box.generic.type = MTYPE_SPINCONTROL;
  3052.         s_no_mines_box.generic.x    = 0;
  3053.         s_no_mines_box.generic.y    = y += 10;
  3054.         s_no_mines_box.generic.name    = "remove mines";
  3055.         s_no_mines_box.generic.callback = DMFlagCallback;
  3056.         s_no_mines_box.itemnames = yes_no_names;
  3057.         s_no_mines_box.curvalue = ( dmflags & DF_NO_MINES ) != 0;
  3058.  
  3059.         s_no_nukes_box.generic.type = MTYPE_SPINCONTROL;
  3060.         s_no_nukes_box.generic.x    = 0;
  3061.         s_no_nukes_box.generic.y    = y += 10;
  3062.         s_no_nukes_box.generic.name    = "remove nukes";
  3063.         s_no_nukes_box.generic.callback = DMFlagCallback;
  3064.         s_no_nukes_box.itemnames = yes_no_names;
  3065.         s_no_nukes_box.curvalue = ( dmflags & DF_NO_NUKES ) != 0;
  3066.  
  3067.         s_stack_double_box.generic.type = MTYPE_SPINCONTROL;
  3068.         s_stack_double_box.generic.x    = 0;
  3069.         s_stack_double_box.generic.y    = y += 10;
  3070.         s_stack_double_box.generic.name    = "2x/4x stacking off";
  3071.         s_stack_double_box.generic.callback = DMFlagCallback;
  3072.         s_stack_double_box.itemnames = yes_no_names;
  3073.         s_stack_double_box.curvalue = ( dmflags & DF_NO_STACK_DOUBLE ) != 0;
  3074.  
  3075.         s_no_spheres_box.generic.type = MTYPE_SPINCONTROL;
  3076.         s_no_spheres_box.generic.x    = 0;
  3077.         s_no_spheres_box.generic.y    = y += 10;
  3078.         s_no_spheres_box.generic.name    = "remove spheres";
  3079.         s_no_spheres_box.generic.callback = DMFlagCallback;
  3080.         s_no_spheres_box.itemnames = yes_no_names;
  3081.         s_no_spheres_box.curvalue = ( dmflags & DF_NO_SPHERES ) != 0;
  3082.  
  3083.     }
  3084. //ROGUE
  3085. //============
  3086.  
  3087.     Menu_AddItem( &s_dmoptions_menu, &s_falls_box );
  3088.     Menu_AddItem( &s_dmoptions_menu, &s_weapons_stay_box );
  3089.     Menu_AddItem( &s_dmoptions_menu, &s_instant_powerups_box );
  3090.     Menu_AddItem( &s_dmoptions_menu, &s_powerups_box );
  3091.     Menu_AddItem( &s_dmoptions_menu, &s_health_box );
  3092.     Menu_AddItem( &s_dmoptions_menu, &s_armor_box );
  3093.     Menu_AddItem( &s_dmoptions_menu, &s_spawn_farthest_box );
  3094.     Menu_AddItem( &s_dmoptions_menu, &s_samelevel_box );
  3095.     Menu_AddItem( &s_dmoptions_menu, &s_force_respawn_box );
  3096.     Menu_AddItem( &s_dmoptions_menu, &s_teamplay_box );
  3097.     Menu_AddItem( &s_dmoptions_menu, &s_allow_exit_box );
  3098.     Menu_AddItem( &s_dmoptions_menu, &s_infinite_ammo_box );
  3099.     Menu_AddItem( &s_dmoptions_menu, &s_fixed_fov_box );
  3100.     Menu_AddItem( &s_dmoptions_menu, &s_quad_drop_box );
  3101.     Menu_AddItem( &s_dmoptions_menu, &s_friendlyfire_box );
  3102.  
  3103. //=======
  3104. //ROGUE
  3105.     if(Developer_searchpath(2) == 2)
  3106.     {
  3107.         Menu_AddItem( &s_dmoptions_menu, &s_no_mines_box );
  3108.         Menu_AddItem( &s_dmoptions_menu, &s_no_nukes_box );
  3109.         Menu_AddItem( &s_dmoptions_menu, &s_stack_double_box );
  3110.         Menu_AddItem( &s_dmoptions_menu, &s_no_spheres_box );
  3111.     }
  3112. //ROGUE
  3113. //=======
  3114.  
  3115.     Menu_Center( &s_dmoptions_menu );
  3116.  
  3117.     // set the original dmflags statusbar
  3118.     DMFlagCallback( 0 );
  3119.     Menu_SetStatusBar( &s_dmoptions_menu, dmoptions_statusbar );
  3120. }
  3121.  
  3122. void DMOptions_MenuDraw(void)
  3123. {
  3124.     Menu_Draw( &s_dmoptions_menu );
  3125. }
  3126.  
  3127. const char *DMOptions_MenuKey( int key )
  3128. {
  3129.     return Default_MenuKey( &s_dmoptions_menu, key );
  3130. }
  3131.  
  3132. void M_Menu_DMOptions_f (void)
  3133. {
  3134.     DMOptions_MenuInit();
  3135.     M_PushMenu( DMOptions_MenuDraw, DMOptions_MenuKey );
  3136. }
  3137.  
  3138. /*
  3139. =============================================================================
  3140.  
  3141. DOWNLOADOPTIONS BOOK MENU
  3142.  
  3143. =============================================================================
  3144. */
  3145. static menuframework_s s_downloadoptions_menu;
  3146.  
  3147. static menuseparator_s    s_download_title;
  3148. static menulist_s    s_allow_download_box;
  3149. static menulist_s    s_allow_download_maps_box;
  3150. static menulist_s    s_allow_download_models_box;
  3151. static menulist_s    s_allow_download_players_box;
  3152. static menulist_s    s_allow_download_sounds_box;
  3153.  
  3154. static void DownloadCallback( void *self )
  3155. {
  3156.     menulist_s *f = ( menulist_s * ) self;
  3157.  
  3158.     if (f == &s_allow_download_box)
  3159.     {
  3160.         Cvar_SetValue("allow_download", f->curvalue);
  3161.     }
  3162.  
  3163.     else if (f == &s_allow_download_maps_box)
  3164.     {
  3165.         Cvar_SetValue("allow_download_maps", f->curvalue);
  3166.     }
  3167.  
  3168.     else if (f == &s_allow_download_models_box)
  3169.     {
  3170.         Cvar_SetValue("allow_download_models", f->curvalue);
  3171.     }
  3172.  
  3173.     else if (f == &s_allow_download_players_box)
  3174.     {
  3175.         Cvar_SetValue("allow_download_players", f->curvalue);
  3176.     }
  3177.  
  3178.     else if (f == &s_allow_download_sounds_box)
  3179.     {
  3180.         Cvar_SetValue("allow_download_sounds", f->curvalue);
  3181.     }
  3182. }
  3183.  
  3184. void DownloadOptions_MenuInit( void )
  3185. {
  3186.     static const char *yes_no_names[] =
  3187.     {
  3188.         "no", "yes", 0
  3189.     };
  3190.     int y = 0;
  3191.  
  3192.     s_downloadoptions_menu.x = viddef.width * 0.50;
  3193.     s_downloadoptions_menu.nitems = 0;
  3194.  
  3195.     s_download_title.generic.type = MTYPE_SEPARATOR;
  3196.     s_download_title.generic.name = "Download Options";
  3197.     s_download_title.generic.x    = 48;
  3198.     s_download_title.generic.y     = y;
  3199.  
  3200.     s_allow_download_box.generic.type = MTYPE_SPINCONTROL;
  3201.     s_allow_download_box.generic.x    = 0;
  3202.     s_allow_download_box.generic.y    = y += 20;
  3203.     s_allow_download_box.generic.name    = "allow downloading";
  3204.     s_allow_download_box.generic.callback = DownloadCallback;
  3205.     s_allow_download_box.itemnames = yes_no_names;
  3206.     s_allow_download_box.curvalue = (Cvar_VariableValue("allow_download") != 0);
  3207.  
  3208.     s_allow_download_maps_box.generic.type = MTYPE_SPINCONTROL;
  3209.     s_allow_download_maps_box.generic.x    = 0;
  3210.     s_allow_download_maps_box.generic.y    = y += 20;
  3211.     s_allow_download_maps_box.generic.name    = "maps";
  3212.     s_allow_download_maps_box.generic.callback = DownloadCallback;
  3213.     s_allow_download_maps_box.itemnames = yes_no_names;
  3214.     s_allow_download_maps_box.curvalue = (Cvar_VariableValue("allow_download_maps") != 0);
  3215.  
  3216.     s_allow_download_players_box.generic.type = MTYPE_SPINCONTROL;
  3217.     s_allow_download_players_box.generic.x    = 0;
  3218.     s_allow_download_players_box.generic.y    = y += 10;
  3219.     s_allow_download_players_box.generic.name    = "player models/skins";
  3220.     s_allow_download_players_box.generic.callback = DownloadCallback;
  3221.     s_allow_download_players_box.itemnames = yes_no_names;
  3222.     s_allow_download_players_box.curvalue = (Cvar_VariableValue("allow_download_players") != 0);
  3223.  
  3224.     s_allow_download_models_box.generic.type = MTYPE_SPINCONTROL;
  3225.     s_allow_download_models_box.generic.x    = 0;
  3226.     s_allow_download_models_box.generic.y    = y += 10;
  3227.     s_allow_download_models_box.generic.name    = "models";
  3228.     s_allow_download_models_box.generic.callback = DownloadCallback;
  3229.     s_allow_download_models_box.itemnames = yes_no_names;
  3230.     s_allow_download_models_box.curvalue = (Cvar_VariableValue("allow_download_models") != 0);
  3231.  
  3232.     s_allow_download_sounds_box.generic.type = MTYPE_SPINCONTROL;
  3233.     s_allow_download_sounds_box.generic.x    = 0;
  3234.     s_allow_download_sounds_box.generic.y    = y += 10;
  3235.     s_allow_download_sounds_box.generic.name    = "sounds";
  3236.     s_allow_download_sounds_box.generic.callback = DownloadCallback;
  3237.     s_allow_download_sounds_box.itemnames = yes_no_names;
  3238.     s_allow_download_sounds_box.curvalue = (Cvar_VariableValue("allow_download_sounds") != 0);
  3239.  
  3240.     Menu_AddItem( &s_downloadoptions_menu, &s_download_title );
  3241.     Menu_AddItem( &s_downloadoptions_menu, &s_allow_download_box );
  3242.     Menu_AddItem( &s_downloadoptions_menu, &s_allow_download_maps_box );
  3243.     Menu_AddItem( &s_downloadoptions_menu, &s_allow_download_players_box );
  3244.     Menu_AddItem( &s_downloadoptions_menu, &s_allow_download_models_box );
  3245.     Menu_AddItem( &s_downloadoptions_menu, &s_allow_download_sounds_box );
  3246.  
  3247.     Menu_Center( &s_downloadoptions_menu );
  3248.  
  3249.     // skip over title
  3250.     if (s_downloadoptions_menu.cursor == 0)
  3251.         s_downloadoptions_menu.cursor = 1;
  3252. }
  3253.  
  3254. void DownloadOptions_MenuDraw(void)
  3255. {
  3256.     Menu_Draw( &s_downloadoptions_menu );
  3257. }
  3258.  
  3259. const char *DownloadOptions_MenuKey( int key )
  3260. {
  3261.     return Default_MenuKey( &s_downloadoptions_menu, key );
  3262. }
  3263.  
  3264. void M_Menu_DownloadOptions_f (void)
  3265. {
  3266.     DownloadOptions_MenuInit();
  3267.     M_PushMenu( DownloadOptions_MenuDraw, DownloadOptions_MenuKey );
  3268. }
  3269. /*
  3270. =============================================================================
  3271.  
  3272. ADDRESS BOOK MENU
  3273.  
  3274. =============================================================================
  3275. */
  3276. #define NUM_ADDRESSBOOK_ENTRIES 9
  3277.  
  3278. static menuframework_s    s_addressbook_menu;
  3279. static menufield_s        s_addressbook_fields[NUM_ADDRESSBOOK_ENTRIES];
  3280.  
  3281. void AddressBook_MenuInit( void )
  3282. {
  3283.     int i;
  3284.  
  3285.     s_addressbook_menu.x = viddef.width / 2 - 142;
  3286.     s_addressbook_menu.y = viddef.height / 2 - 58;
  3287.     s_addressbook_menu.nitems = 0;
  3288.  
  3289.     for ( i = 0; i < NUM_ADDRESSBOOK_ENTRIES; i++ )
  3290.     {
  3291.         cvar_t *adr;
  3292.         char buffer[20];
  3293.  
  3294.         Com_sprintf( buffer, sizeof( buffer ), "adr%d", i );
  3295.  
  3296.         adr = Cvar_Get( buffer, "", CVAR_ARCHIVE );
  3297.  
  3298.         s_addressbook_fields[i].generic.type = MTYPE_FIELD;
  3299.         s_addressbook_fields[i].generic.name = 0;
  3300.         s_addressbook_fields[i].generic.callback = 0;
  3301.         s_addressbook_fields[i].generic.x        = 0;
  3302.         s_addressbook_fields[i].generic.y        = i * 18 + 0;
  3303.         s_addressbook_fields[i].generic.localdata[0] = i;
  3304.         s_addressbook_fields[i].cursor            = 0;
  3305.         s_addressbook_fields[i].length            = 60;
  3306.         s_addressbook_fields[i].visible_length    = 30;
  3307.  
  3308.         strcpy( s_addressbook_fields[i].buffer, adr->string );
  3309.  
  3310.         Menu_AddItem( &s_addressbook_menu, &s_addressbook_fields[i] );
  3311.     }
  3312. }
  3313.  
  3314. const char *AddressBook_MenuKey( int key )
  3315. {
  3316.     if ( key == K_ESCAPE )
  3317.     {
  3318.         int index;
  3319.         char buffer[20];
  3320.  
  3321.         for ( index = 0; index < NUM_ADDRESSBOOK_ENTRIES; index++ )
  3322.         {
  3323.             Com_sprintf( buffer, sizeof( buffer ), "adr%d", index );
  3324.             Cvar_Set( buffer, s_addressbook_fields[index].buffer );
  3325.         }
  3326.     }
  3327.     return Default_MenuKey( &s_addressbook_menu, key );
  3328. }
  3329.  
  3330. void AddressBook_MenuDraw(void)
  3331. {
  3332.     M_Banner( "m_banner_addressbook" );
  3333.     Menu_Draw( &s_addressbook_menu );
  3334. }
  3335.  
  3336. void M_Menu_AddressBook_f(void)
  3337. {
  3338.     AddressBook_MenuInit();
  3339.     M_PushMenu( AddressBook_MenuDraw, AddressBook_MenuKey );
  3340. }
  3341.  
  3342. /*
  3343. =============================================================================
  3344.  
  3345. PLAYER CONFIG MENU
  3346.  
  3347. =============================================================================
  3348. */
  3349. static menuframework_s    s_player_config_menu;
  3350. static menufield_s        s_player_name_field;
  3351. static menulist_s        s_player_model_box;
  3352. static menulist_s        s_player_skin_box;
  3353. static menulist_s        s_player_handedness_box;
  3354. static menulist_s        s_player_rate_box;
  3355. static menuseparator_s    s_player_skin_title;
  3356. static menuseparator_s    s_player_model_title;
  3357. static menuseparator_s    s_player_hand_title;
  3358. static menuseparator_s    s_player_rate_title;
  3359. static menuaction_s        s_player_download_action;
  3360.  
  3361. #define MAX_DISPLAYNAME 16
  3362. #define MAX_PLAYERMODELS 1024
  3363.  
  3364. typedef struct
  3365. {
  3366.     int        nskins;
  3367.     char    **skindisplaynames;
  3368.     char    displayname[MAX_DISPLAYNAME];
  3369.     char    directory[MAX_QPATH];
  3370. } playermodelinfo_s;
  3371.  
  3372. static playermodelinfo_s s_pmi[MAX_PLAYERMODELS];
  3373. static char *s_pmnames[MAX_PLAYERMODELS];
  3374. static int s_numplayermodels;
  3375.  
  3376. static int rate_tbl[] = { 2500, 3200, 5000, 10000, 25000, 0 };
  3377. static const char *rate_names[] = { "28.8 Modem", "33.6 Modem", "Single ISDN",
  3378.     "Dual ISDN/Cable", "T1/LAN", "User defined", 0 };
  3379.  
  3380. void DownloadOptionsFunc( void *self )
  3381. {
  3382.     M_Menu_DownloadOptions_f();
  3383. }
  3384.  
  3385. static void HandednessCallback( void *unused )
  3386. {
  3387.     Cvar_SetValue( "hand", s_player_handedness_box.curvalue );
  3388. }
  3389.  
  3390. static void RateCallback( void *unused )
  3391. {
  3392.     if (s_player_rate_box.curvalue != sizeof(rate_tbl) / sizeof(*rate_tbl) - 1)
  3393.         Cvar_SetValue( "rate", rate_tbl[s_player_rate_box.curvalue] );
  3394. }
  3395.  
  3396. static void ModelCallback( void *unused )
  3397. {
  3398.     s_player_skin_box.itemnames = s_pmi[s_player_model_box.curvalue].skindisplaynames;
  3399.     s_player_skin_box.curvalue = 0;
  3400. }
  3401.  
  3402. static void FreeFileList( char **list, int n )
  3403. {
  3404.     int i;
  3405.  
  3406.     for ( i = 0; i < n; i++ )
  3407.     {
  3408.         if ( list[i] )
  3409.         {
  3410.             free( list[i] );
  3411.             list[i] = 0;
  3412.         }
  3413.     }
  3414.     free( list );
  3415. }
  3416.  
  3417. static qboolean IconOfSkinExists( char *skin, char **pcxfiles, int npcxfiles )
  3418. {
  3419.     int i;
  3420.     char scratch[1024];
  3421.  
  3422.     strcpy( scratch, skin );
  3423.     *strrchr( scratch, '.' ) = 0;
  3424.     strcat( scratch, "_i.pcx" );
  3425.  
  3426.     for ( i = 0; i < npcxfiles; i++ )
  3427.     {
  3428.         if ( strcmp( pcxfiles[i], scratch ) == 0 )
  3429.             return true;
  3430.     }
  3431.  
  3432.     return false;
  3433. }
  3434.  
  3435. static void PlayerConfig_ScanDirectories( void )
  3436. {
  3437.     char findname[1024];
  3438.     char scratch[1024];
  3439.     int ndirs = 0, npms = 0;
  3440.     char **dirnames;
  3441.     char *path = NULL;
  3442.     int i;
  3443.  
  3444.     extern char **FS_ListFiles( char *, int *, unsigned, unsigned );
  3445.  
  3446.     s_numplayermodels = 0;
  3447.  
  3448.     /*
  3449.     ** get a list of directories
  3450.     */
  3451.     do 
  3452.     {
  3453.         path = FS_NextPath( path );
  3454.         Com_sprintf( findname, sizeof(findname), "%s/players/*.*", path );
  3455.  
  3456.         if ( ( dirnames = FS_ListFiles( findname, &ndirs, SFF_SUBDIR, 0 ) ) != 0 )
  3457.             break;
  3458.     } while ( path );
  3459.  
  3460.     if ( !dirnames )
  3461.         return;
  3462.  
  3463.     /*
  3464.     ** go through the subdirectories
  3465.     */
  3466.     npms = ndirs;
  3467.     if ( npms > MAX_PLAYERMODELS )
  3468.         npms = MAX_PLAYERMODELS;
  3469.  
  3470.     for ( i = 0; i < npms; i++ )
  3471.     {
  3472.         int k, s;
  3473.         char *a, *b, *c;
  3474.         char **pcxnames;
  3475.         char **skinnames;
  3476.         int npcxfiles;
  3477.         int nskins = 0;
  3478.  
  3479.         if ( dirnames[i] == 0 )
  3480.             continue;
  3481.  
  3482.         // verify the existence of tris.md2
  3483.         strcpy( scratch, dirnames[i] );
  3484.         strcat( scratch, "/tris.md2" );
  3485.         if ( !Sys_FindFirst( scratch, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM ) )
  3486.         {
  3487.             free( dirnames[i] );
  3488.             dirnames[i] = 0;
  3489.             Sys_FindClose();
  3490.             continue;
  3491.         }
  3492.         Sys_FindClose();
  3493.  
  3494.         // verify the existence of at least one pcx skin
  3495.         strcpy( scratch, dirnames[i] );
  3496.         strcat( scratch, "/*.pcx" );
  3497.         pcxnames = FS_ListFiles( scratch, &npcxfiles, 0, SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM );
  3498.  
  3499.         if ( !pcxnames )
  3500.         {
  3501.             free( dirnames[i] );
  3502.             dirnames[i] = 0;
  3503.             continue;
  3504.         }
  3505.  
  3506.         // count valid skins, which consist of a skin with a matching "_i" icon
  3507.         for ( k = 0; k < npcxfiles-1; k++ )
  3508.         {
  3509.             if ( !strstr( pcxnames[k], "_i.pcx" ) )
  3510.             {
  3511.                 if ( IconOfSkinExists( pcxnames[k], pcxnames, npcxfiles - 1 ) )
  3512.                 {
  3513.                     nskins++;
  3514.                 }
  3515.             }
  3516.         }
  3517.         if ( !nskins )
  3518.             continue;
  3519.  
  3520.         skinnames = malloc( sizeof( char * ) * ( nskins + 1 ) );
  3521.         memset( skinnames, 0, sizeof( char * ) * ( nskins + 1 ) );
  3522.  
  3523.         // copy the valid skins
  3524.         for ( s = 0, k = 0; k < npcxfiles-1; k++ )
  3525.         {
  3526.             char *a, *b, *c;
  3527.  
  3528.             if ( !strstr( pcxnames[k], "_i.pcx" ) )
  3529.             {
  3530.                 if ( IconOfSkinExists( pcxnames[k], pcxnames, npcxfiles - 1 ) )
  3531.                 {
  3532.                     a = strrchr( pcxnames[k], '/' );
  3533.                     b = strrchr( pcxnames[k], '\\' );
  3534.  
  3535.                     if ( a > b )
  3536.                         c = a;
  3537.                     else
  3538.                         c = b;
  3539.  
  3540.                     strcpy( scratch, c + 1 );
  3541.  
  3542.                     if ( strrchr( scratch, '.' ) )
  3543.                         *strrchr( scratch, '.' ) = 0;
  3544.  
  3545.                     skinnames[s] = strdup( scratch );
  3546.                     s++;
  3547.                 }
  3548.             }
  3549.         }
  3550.  
  3551.         // at this point we have a valid player model
  3552.         s_pmi[s_numplayermodels].nskins = nskins;
  3553.         s_pmi[s_numplayermodels].skindisplaynames = skinnames;
  3554.  
  3555.         // make short name for the model
  3556.         a = strrchr( dirnames[i], '/' );
  3557.         b = strrchr( dirnames[i], '\\' );
  3558.  
  3559.         if ( a > b )
  3560.             c = a;
  3561.         else
  3562.             c = b;
  3563.  
  3564.         strncpy( s_pmi[s_numplayermodels].displayname, c + 1, MAX_DISPLAYNAME-1 );
  3565.         strcpy( s_pmi[s_numplayermodels].directory, c + 1 );
  3566.  
  3567.         FreeFileList( pcxnames, npcxfiles );
  3568.  
  3569.         s_numplayermodels++;
  3570.     }
  3571.     if ( dirnames )
  3572.         FreeFileList( dirnames, ndirs );
  3573. }
  3574.  
  3575. static int pmicmpfnc( const void *_a, const void *_b )
  3576. {
  3577.     const playermodelinfo_s *a = ( const playermodelinfo_s * ) _a;
  3578.     const playermodelinfo_s *b = ( const playermodelinfo_s * ) _b;
  3579.  
  3580.     /*
  3581.     ** sort by male, female, then alphabetical
  3582.     */
  3583.     if ( strcmp( a->directory, "male" ) == 0 )
  3584.         return -1;
  3585.     else if ( strcmp( b->directory, "male" ) == 0 )
  3586.         return 1;
  3587.  
  3588.     if ( strcmp( a->directory, "female" ) == 0 )
  3589.         return -1;
  3590.     else if ( strcmp( b->directory, "female" ) == 0 )
  3591.         return 1;
  3592.  
  3593.     return strcmp( a->directory, b->directory );
  3594. }
  3595.  
  3596.  
  3597. qboolean PlayerConfig_MenuInit( void )
  3598. {
  3599.     extern cvar_t *name;
  3600.     extern cvar_t *team;
  3601.     extern cvar_t *skin;
  3602.     char currentdirectory[1024];
  3603.     char currentskin[1024];
  3604.     int i = 0;
  3605.  
  3606.     int currentdirectoryindex = 0;
  3607.     int currentskinindex = 0;
  3608.  
  3609.     cvar_t *hand = Cvar_Get( "hand", "0", CVAR_USERINFO | CVAR_ARCHIVE );
  3610.  
  3611.     static const char *handedness[] = { "right", "left", "center", 0 };
  3612.  
  3613.     PlayerConfig_ScanDirectories();
  3614.  
  3615.     if (s_numplayermodels == 0)
  3616.         return false;
  3617.  
  3618.     if ( hand->value < 0 || hand->value > 2 )
  3619.         Cvar_SetValue( "hand", 0 );
  3620.  
  3621.     strcpy( currentdirectory, skin->string );
  3622.  
  3623.     if ( strchr( currentdirectory, '/' ) )
  3624.     {
  3625.         strcpy( currentskin, strchr( currentdirectory, '/' ) + 1 );
  3626.         *strchr( currentdirectory, '/' ) = 0;
  3627.     }
  3628.     else if ( strchr( currentdirectory, '\\' ) )
  3629.     {
  3630.         strcpy( currentskin, strchr( currentdirectory, '\\' ) + 1 );
  3631.         *strchr( currentdirectory, '\\' ) = 0;
  3632.     }
  3633.     else
  3634.     {
  3635.         strcpy( currentdirectory, "male" );
  3636.         strcpy( currentskin, "grunt" );
  3637.     }
  3638.  
  3639.     qsort( s_pmi, s_numplayermodels, sizeof( s_pmi[0] ), pmicmpfnc );
  3640.  
  3641.     memset( s_pmnames, 0, sizeof( s_pmnames ) );
  3642.     for ( i = 0; i < s_numplayermodels; i++ )
  3643.     {
  3644.         s_pmnames[i] = s_pmi[i].displayname;
  3645.         if ( Q_stricmp( s_pmi[i].directory, currentdirectory ) == 0 )
  3646.         {
  3647.             int j;
  3648.  
  3649.             currentdirectoryindex = i;
  3650.  
  3651.             for ( j = 0; j < s_pmi[i].nskins; j++ )
  3652.             {
  3653.                 if ( Q_stricmp( s_pmi[i].skindisplaynames[j], currentskin ) == 0 )
  3654.                 {
  3655.                     currentskinindex = j;
  3656.                     break;
  3657.                 }
  3658.             }
  3659.         }
  3660.     }
  3661.  
  3662.     s_player_config_menu.x = viddef.width / 2 - 95; 
  3663.     s_player_config_menu.y = viddef.height / 2 - 97;
  3664.     s_player_config_menu.nitems = 0;
  3665.  
  3666.     s_player_name_field.generic.type = MTYPE_FIELD;
  3667.     s_player_name_field.generic.name = "name";
  3668.     s_player_name_field.generic.callback = 0;
  3669.     s_player_name_field.generic.x        = 0;
  3670.     s_player_name_field.generic.y        = 0;
  3671.     s_player_name_field.length    = 20;
  3672.     s_player_name_field.visible_length = 20;
  3673.     strcpy( s_player_name_field.buffer, name->string );
  3674.     s_player_name_field.cursor = strlen( name->string );
  3675.  
  3676.     s_player_model_title.generic.type = MTYPE_SEPARATOR;
  3677.     s_player_model_title.generic.name = "model";
  3678.     s_player_model_title.generic.x    = -8;
  3679.     s_player_model_title.generic.y     = 60;
  3680.  
  3681.     s_player_model_box.generic.type = MTYPE_SPINCONTROL;
  3682.     s_player_model_box.generic.x    = -56;
  3683.     s_player_model_box.generic.y    = 70;
  3684.     s_player_model_box.generic.callback = ModelCallback;
  3685.     s_player_model_box.generic.cursor_offset = -48;
  3686.     s_player_model_box.curvalue = currentdirectoryindex;
  3687.     s_player_model_box.itemnames = s_pmnames;
  3688.  
  3689.     s_player_skin_title.generic.type = MTYPE_SEPARATOR;
  3690.     s_player_skin_title.generic.name = "skin";
  3691.     s_player_skin_title.generic.x    = -16;
  3692.     s_player_skin_title.generic.y     = 84;
  3693.  
  3694.     s_player_skin_box.generic.type = MTYPE_SPINCONTROL;
  3695.     s_player_skin_box.generic.x    = -56;
  3696.     s_player_skin_box.generic.y    = 94;
  3697.     s_player_skin_box.generic.name    = 0;
  3698.     s_player_skin_box.generic.callback = 0;
  3699.     s_player_skin_box.generic.cursor_offset = -48;
  3700.     s_player_skin_box.curvalue = currentskinindex;
  3701.     s_player_skin_box.itemnames = s_pmi[currentdirectoryindex].skindisplaynames;
  3702.  
  3703.     s_player_hand_title.generic.type = MTYPE_SEPARATOR;
  3704.     s_player_hand_title.generic.name = "handedness";
  3705.     s_player_hand_title.generic.x    = 32;
  3706.     s_player_hand_title.generic.y     = 108;
  3707.  
  3708.     s_player_handedness_box.generic.type = MTYPE_SPINCONTROL;
  3709.     s_player_handedness_box.generic.x    = -56;
  3710.     s_player_handedness_box.generic.y    = 118;
  3711.     s_player_handedness_box.generic.name    = 0;
  3712.     s_player_handedness_box.generic.cursor_offset = -48;
  3713.     s_player_handedness_box.generic.callback = HandednessCallback;
  3714.     s_player_handedness_box.curvalue = Cvar_VariableValue( "hand" );
  3715.     s_player_handedness_box.itemnames = handedness;
  3716.  
  3717.     for (i = 0; i < sizeof(rate_tbl) / sizeof(*rate_tbl) - 1; i++)
  3718.         if (Cvar_VariableValue("rate") == rate_tbl[i])
  3719.             break;
  3720.  
  3721.     s_player_rate_title.generic.type = MTYPE_SEPARATOR;
  3722.     s_player_rate_title.generic.name = "connect speed";
  3723.     s_player_rate_title.generic.x    = 56;
  3724.     s_player_rate_title.generic.y     = 156;
  3725.  
  3726.     s_player_rate_box.generic.type = MTYPE_SPINCONTROL;
  3727.     s_player_rate_box.generic.x    = -56;
  3728.     s_player_rate_box.generic.y    = 166;
  3729.     s_player_rate_box.generic.name    = 0;
  3730.     s_player_rate_box.generic.cursor_offset = -48;
  3731.     s_player_rate_box.generic.callback = RateCallback;
  3732.     s_player_rate_box.curvalue = i;
  3733.     s_player_rate_box.itemnames = rate_names;
  3734.  
  3735.     s_player_download_action.generic.type = MTYPE_ACTION;
  3736.     s_player_download_action.generic.name    = "download options";
  3737.     s_player_download_action.generic.flags= QMF_LEFT_JUSTIFY;
  3738.     s_player_download_action.generic.x    = -24;
  3739.     s_player_download_action.generic.y    = 186;
  3740.     s_player_download_action.generic.statusbar = NULL;
  3741.     s_player_download_action.generic.callback = DownloadOptionsFunc;
  3742.  
  3743.     Menu_AddItem( &s_player_config_menu, &s_player_name_field );
  3744.     Menu_AddItem( &s_player_config_menu, &s_player_model_title );
  3745.     Menu_AddItem( &s_player_config_menu, &s_player_model_box );
  3746.     if ( s_player_skin_box.itemnames )
  3747.     {
  3748.         Menu_AddItem( &s_player_config_menu, &s_player_skin_title );
  3749.         Menu_AddItem( &s_player_config_menu, &s_player_skin_box );
  3750.     }
  3751.     Menu_AddItem( &s_player_config_menu, &s_player_hand_title );
  3752.     Menu_AddItem( &s_player_config_menu, &s_player_handedness_box );
  3753.     Menu_AddItem( &s_player_config_menu, &s_player_rate_title );
  3754.     Menu_AddItem( &s_player_config_menu, &s_player_rate_box );
  3755.     Menu_AddItem( &s_player_config_menu, &s_player_download_action );
  3756.  
  3757.     return true;
  3758. }
  3759.  
  3760. void PlayerConfig_MenuDraw( void )
  3761. {
  3762.     extern float CalcFov( float fov_x, float w, float h );
  3763.     refdef_t refdef;
  3764.     char scratch[MAX_QPATH];
  3765.  
  3766.     memset( &refdef, 0, sizeof( refdef ) );
  3767.  
  3768.     refdef.x = viddef.width / 2;
  3769.     refdef.y = viddef.height / 2 - 72;
  3770.     refdef.width = 144;
  3771.     refdef.height = 168;
  3772.     refdef.fov_x = 40;
  3773.     refdef.fov_y = CalcFov( refdef.fov_x, refdef.width, refdef.height );
  3774.     refdef.time = cls.realtime*0.001;
  3775.  
  3776.     if ( s_pmi[s_player_model_box.curvalue].skindisplaynames )
  3777.     {
  3778.         static int yaw;
  3779.         int maxframe = 29;
  3780.         entity_t entity;
  3781.  
  3782.         memset( &entity, 0, sizeof( entity ) );
  3783.  
  3784.         Com_sprintf( scratch, sizeof( scratch ), "players/%s/tris.md2", s_pmi[s_player_model_box.curvalue].directory );
  3785.         entity.model = R_RegisterModel( scratch );
  3786.         Com_sprintf( scratch, sizeof( scratch ), "players/%s/%s.pcx", s_pmi[s_player_model_box.curvalue].directory, s_pmi[s_player_model_box.curvalue].skindisplaynames[s_player_skin_box.curvalue] );
  3787.         entity.skin = R_RegisterSkin( scratch );
  3788.         entity.flags = RF_FULLBRIGHT;
  3789.         entity.origin[0] = 80;
  3790.         entity.origin[1] = 0;
  3791.         entity.origin[2] = 0;
  3792.         VectorCopy( entity.origin, entity.oldorigin );
  3793.         entity.frame = 0;
  3794.         entity.oldframe = 0;
  3795.         entity.backlerp = 0.0;
  3796.         entity.angles[1] = yaw++;
  3797.         if ( ++yaw > 360 )
  3798.             yaw -= 360;
  3799.  
  3800.         refdef.areabits = 0;
  3801.         refdef.num_entities = 1;
  3802.         refdef.entities = &entity;
  3803.         refdef.lightstyles = 0;
  3804.         refdef.rdflags = RDF_NOWORLDMODEL;
  3805.  
  3806.         Menu_Draw( &s_player_config_menu );
  3807.  
  3808.         M_DrawTextBox( ( refdef.x ) * ( 320.0F / viddef.width ) - 8, ( viddef.height / 2 ) * ( 240.0F / viddef.height) - 77, refdef.width / 8, refdef.height / 8 );
  3809.         refdef.height += 4;
  3810.  
  3811.         R_RenderFrame( &refdef );
  3812.  
  3813.         Com_sprintf( scratch, sizeof( scratch ), "/players/%s/%s_i.pcx", 
  3814.             s_pmi[s_player_model_box.curvalue].directory,
  3815.             s_pmi[s_player_model_box.curvalue].skindisplaynames[s_player_skin_box.curvalue] );
  3816.         Draw_Pic( s_player_config_menu.x - 40, refdef.y, scratch );
  3817.     }
  3818. }
  3819.  
  3820. const char *PlayerConfig_MenuKey (int key)
  3821. {
  3822.     int i;
  3823.  
  3824.     if ( key == K_ESCAPE )
  3825.     {
  3826.         char scratch[1024];
  3827.  
  3828.         Cvar_Set( "name", s_player_name_field.buffer );
  3829.  
  3830.         Com_sprintf( scratch, sizeof( scratch ), "%s/%s", 
  3831.             s_pmi[s_player_model_box.curvalue].directory, 
  3832.             s_pmi[s_player_model_box.curvalue].skindisplaynames[s_player_skin_box.curvalue] );
  3833.  
  3834.         Cvar_Set( "skin", scratch );
  3835.  
  3836.         for ( i = 0; i < s_numplayermodels; i++ )
  3837.         {
  3838.             int j;
  3839.  
  3840.             for ( j = 0; j < s_pmi[i].nskins; j++ )
  3841.             {
  3842.                 if ( s_pmi[i].skindisplaynames[j] )
  3843.                     free( s_pmi[i].skindisplaynames[j] );
  3844.                 s_pmi[i].skindisplaynames[j] = 0;
  3845.             }
  3846.             free( s_pmi[i].skindisplaynames );
  3847.             s_pmi[i].skindisplaynames = 0;
  3848.             s_pmi[i].nskins = 0;
  3849.         }
  3850.     }
  3851.     return Default_MenuKey( &s_player_config_menu, key );
  3852. }
  3853.  
  3854.  
  3855. void M_Menu_PlayerConfig_f (void)
  3856. {
  3857.     if (!PlayerConfig_MenuInit())
  3858.     {
  3859.         Menu_SetStatusBar( &s_multiplayer_menu, "No valid player models found" );
  3860.         return;
  3861.     }
  3862.     Menu_SetStatusBar( &s_multiplayer_menu, NULL );
  3863.     M_PushMenu( PlayerConfig_MenuDraw, PlayerConfig_MenuKey );
  3864. }
  3865.  
  3866.  
  3867. /*
  3868. =======================================================================
  3869.  
  3870. GALLERY MENU
  3871.  
  3872. =======================================================================
  3873. */
  3874. #if 0
  3875. void M_Menu_Gallery_f( void )
  3876. {
  3877.     extern void Gallery_MenuDraw( void );
  3878.     extern const char *Gallery_MenuKey( int key );
  3879.  
  3880.     M_PushMenu( Gallery_MenuDraw, Gallery_MenuKey );
  3881. }
  3882. #endif
  3883.  
  3884. /*
  3885. =======================================================================
  3886.  
  3887. QUIT MENU
  3888.  
  3889. =======================================================================
  3890. */
  3891.  
  3892. const char *M_Quit_Key (int key)
  3893. {
  3894.     switch (key)
  3895.     {
  3896.     case K_ESCAPE:
  3897.     case 'n':
  3898.     case 'N':
  3899.         M_PopMenu ();
  3900.         break;
  3901.  
  3902.     case 'Y':
  3903.     case 'y':
  3904.         cls.key_dest = key_console;
  3905.         CL_Quit_f ();
  3906.         break;
  3907.  
  3908.     default:
  3909.         break;
  3910.     }
  3911.  
  3912.     return NULL;
  3913.  
  3914. }
  3915.  
  3916.  
  3917. void M_Quit_Draw (void)
  3918. {
  3919.     int        w, h;
  3920.  
  3921.     Draw_GetPicSize (&w, &h, "quit");
  3922.     Draw_Pic ( (viddef.width-w)/2, (viddef.height-h)/2, "quit");
  3923. }
  3924.  
  3925.  
  3926. void M_Menu_Quit_f (void)
  3927. {
  3928.     M_PushMenu (M_Quit_Draw, M_Quit_Key);
  3929. }
  3930.  
  3931.  
  3932.  
  3933. //=============================================================================
  3934. /* Menu Subsystem */
  3935.  
  3936.  
  3937. /*
  3938. =================
  3939. M_Init
  3940. =================
  3941. */
  3942. void M_Init (void)
  3943. {
  3944.     Cmd_AddCommand ("menu_main", M_Menu_Main_f);
  3945.     Cmd_AddCommand ("menu_game", M_Menu_Game_f);
  3946.         Cmd_AddCommand ("menu_loadgame", M_Menu_LoadGame_f);
  3947.         Cmd_AddCommand ("menu_savegame", M_Menu_SaveGame_f);
  3948.         Cmd_AddCommand ("menu_joinserver", M_Menu_JoinServer_f);
  3949.             Cmd_AddCommand ("menu_addressbook", M_Menu_AddressBook_f);
  3950.         Cmd_AddCommand ("menu_startserver", M_Menu_StartServer_f);
  3951.             Cmd_AddCommand ("menu_dmoptions", M_Menu_DMOptions_f);
  3952.         Cmd_AddCommand ("menu_playerconfig", M_Menu_PlayerConfig_f);
  3953.             Cmd_AddCommand ("menu_downloadoptions", M_Menu_DownloadOptions_f);
  3954.         Cmd_AddCommand ("menu_credits", M_Menu_Credits_f );
  3955.     Cmd_AddCommand ("menu_multiplayer", M_Menu_Multiplayer_f );
  3956.     Cmd_AddCommand ("menu_video", M_Menu_Video_f);
  3957.     Cmd_AddCommand ("menu_options", M_Menu_Options_f);
  3958.         Cmd_AddCommand ("menu_keys", M_Menu_Keys_f);
  3959.     Cmd_AddCommand ("menu_quit", M_Menu_Quit_f);
  3960. }
  3961.  
  3962.  
  3963. /*
  3964. =================
  3965. M_Draw
  3966. =================
  3967. */
  3968. void M_Draw (void)
  3969. {
  3970.     if (cls.key_dest != key_menu)
  3971.         return;
  3972.  
  3973.     // repaint everything next frame
  3974.     SCR_DirtyScreen ();
  3975.  
  3976.     // dim everything behind it down
  3977.     if (cl.cinematictime > 0)
  3978.         Draw_Fill (0,0,viddef.width, viddef.height, 0);
  3979.     else
  3980.         Draw_FadeScreen ();
  3981.  
  3982.     m_drawfunc ();
  3983.  
  3984.     // delay playing the enter sound until after the
  3985.     // menu has been drawn, to avoid delay while
  3986.     // caching images
  3987.     if (m_entersound)
  3988.     {
  3989.         S_StartLocalSound( menu_in_sound );
  3990.         m_entersound = false;
  3991.     }
  3992. }
  3993.  
  3994.  
  3995. /*
  3996. =================
  3997. M_Keydown
  3998. =================
  3999. */
  4000. void M_Keydown (int key)
  4001. {
  4002.     const char *s;
  4003.  
  4004.     if (m_keyfunc)
  4005.         if ( ( s = m_keyfunc( key ) ) != 0 )
  4006.             S_StartLocalSound( ( char * ) s );
  4007. }
  4008.  
  4009.  
  4010.